Error Handling
Every single error caused by your middleware will be caught by grammY. You should install a custom error handler to handle errors.
Most importantly, this section will teach you how to catch errors that can be thrown.
Afterwards, we will look at all three types of errors that your bot can encounter.
Name | Purpose |
---|---|
Bot | Error object that wraps any error thrown in your middleware (for example, the two errors below) |
Grammy | Thrown if the Bot API server returns ok: , indicating that your API request was invalid and failed |
Http | Thrown if the Bot API server could not be reached |
A more advanced error handling mechanism can be found down here.
Catching Errors
How you catch errors will depend on your setup.
Long Polling
If you run your bot via bot
, or if you are using grammY runner, then you should install an error handler via bot
.
grammY has a default error handler installed that stops the bot if it was started by bot
. It then re-throws the error. It depends on the platform what will happen next. That is why you should install an error handler via bot
.
Example:
bot.catch((err) => {
const ctx = err.ctx;
console.error(`Error while handling update ${ctx.update.update_id}:`);
const e = err.error;
if (e instanceof GrammyError) {
console.error("Error in request:", e.description);
} else if (e instanceof HttpError) {
console.error("Could not contact Telegram:", e);
} else {
console.error("Unknown error:", e);
}
});
2
3
4
5
6
7
8
9
10
11
12
Webhooks
If you run your bot via webhooks, grammY will pass the error on to the web framework that you use, e.g. express
. You should handle errors according to the conventions of that framework.
The BotError
Object
The Bot
object bundles up a thrown error with the corresponding context object that caused the error to be thrown. This works as follows.
Whatever error occurs while processing an update, grammY will catch the thrown error for you. It is often useful to access the context object that caused the error.
grammY does not touch the thrown error in any way, but instead wraps it into an instance of Bot
. Given that object is named err
, you can then access the original error via err
. You can access the respective context object via err
.
Check out the Bot
class in the grammY API Reference.
The GrammyError
Object
If an API method like send
fails, grammY will throw a Grammy
. Note that also Grammy
instances will be wrapped in Bot
objects if they are thrown in middleware.
A thrown Grammy
indicates that the corresponding API request failed. The error provides access to the error code returned by the Telegram backend, as well as the description.
Check out the Grammy
class in the grammY API Reference.
The HttpError
Object
An Http
is thrown if a network request fails. This means that grammY was unable to contact the Bot API server. The error object holds information about why the request failed, which are available under the error
property.
You will rarely see this kind of error, unless your network infrastructure is unstable, or the Bot API server of your bot is temporarily offline.
Note that if the Bot API server can be contacted, but it returns
ok:
for a given method call, afalse Grammy
is thrown instead.Error
Check out the Http
class in the grammY API Reference.
Error Boundaries
This is an advanced topic that is mostly useful for larger bots. If you are relatively new to grammY, simply skip the remainder of this section.
If you divide your code base into different parts, error boundaries allow you install different error handlers for different parts of your middleware. They achieve this by letting you fence errors in a part of your middleware. In other words, if an error is thrown in a specially protected part of middleware, it will not be able to escape from that part of the middleware system. Instead, a dedicated error handler is invoked, and the surrounded part of the middleware pretends to complete successfully. This is a feature of grammY’s middleware system, so error boundaries don’t care whether you’re running your bot with webhooks or long polling.
Optionally, you may choose to instead let the middleware execution resume normally after the error was handled, continuing right outside the error boundary. In that case, the fenced middleware does not only act as if it had completed successfully, but it also passes on the control flow to the next middleware that was installed after the error boundary. Thus, it looks like the middleware inside the error boundary has called next
.
const bot = new Bot("");
bot.use(/* A */);
bot.use(/* B */);
const composer = new Composer();
composer.use(/* X */);
composer.use(/* Y */);
composer.use(/* Z */);
bot.errorBoundary(boundaryHandler /* , Q */).use(composer);
bot.use(/* C */);
bot.use(/* D */);
bot.catch(errorHandler);
function boundaryHandler(err: BotError, next: NextFunction) {
console.error("Error in Q, X, Y, or Z!", err);
/*
* You could call `next` if you want to run
* the middleware at C in case of an error:
*/
// await next()
}
function errorHandler(err: BotError) {
console.error("Error in A, B, C, or D!", err);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
In the above example, the boundary
will be invoked for
- all middlewares that are passed to
bot
after.error Boundary boundary
(i.e.Handler Q
), and - all middlewares that are installed on subsequently installed composer instances (i.e.
X
,Y
, andZ
).
Regarding point 2, you may want to skip ahead to the advanced explanation of middleware to learn how chaining works in grammY.
You can also apply an error boundary to a composer without calling bot
:
const composer = new Composer();
const protected = composer.errorBoundary(boundaryHandler);
protected.use(/* B */);
bot.use(composer);
bot.use(/* C */);
bot.catch(errorHandler);
function boundaryHandler(err: BotError, next: NextFunction) {
console.error("Error in B!", err);
}
function errorHandler(err: BotError) {
console.error("Error in C!", err);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
The boundary
of the above example will be invoked for middlewares bound to protected
.
If you actively want the error to cross a boundary (that is, pass it outside), you can re-throw the error inside your error handler. The error will then be passed to the next surrounding boundary.
In a sense, you can regard the error handler installed via bot
as the outermost error boundary.