Understand how Telebot handles errors, configure global error callbacks, and write resilient production bots.
Telebot surfaces errors from handlers, the Telegram API, and the bot internals through a unified pipeline. This page explains each layer and how to configure them.
When a handler returns a non-nil error, Telebot passes it to the OnError callback that was configured in Settings. If OnError is not set, the default implementation logs the error with the update ID:
// From bot.go — the default fallbackvar defaultOnError = func(err error, c Context) { if c != nil { log.Println(c.Update().ID, err) } else { log.Println(err) }}
The bot does not crash on handler errors. Each update is processed independently and errors are reported, then discarded.
Settings.OnError is a function with the signature func(error, Context). The Context parameter may be nil when the error originates outside a handler (for example, during polling).
// Settings.OnError is a callback function that will get called on errors// resulted from the handler. It is used as post-middleware function.// Notice that context can be nil.OnError func(error, Context)
b, err := tele.NewBot(tele.Settings{ Token: os.Getenv("TOKEN"), Poller: &tele.LongPoller{Timeout: 10 * time.Second}, OnError: func(err error, c tele.Context) { if c != nil { log.Printf("handler error for update %d: %v", c.Update().ID, err) } else { log.Printf("bot error: %v", err) } // Optionally report to an external service sentry.CaptureException(err) },})
You can also call the error handler manually at any time:
// OnError triggers the configured error callback.func (b *Bot) OnError(err error, c Context)
The telebot package defines sentinel errors for common bot-level failure cases:
var ( ErrBadRecipient = errors.New("telebot: recipient is nil") ErrUnsupportedWhat = errors.New("telebot: unsupported what argument") ErrCouldNotUpdate = errors.New("telebot: could not fetch new updates") ErrTrueResult = errors.New("telebot: result is True") ErrBadContext = errors.New("telebot: context does not contain message"))
Error
When it occurs
ErrBadRecipient
b.Send was called with a nil recipient
ErrUnsupportedWhat
The what argument passed to Send/Reply is not a string or Sendable
ErrCouldNotUpdate
The long poller could not fetch updates from Telegram
ErrTrueResult
Telegram responded with {"ok":true,"result":true} instead of an object
ErrBadContext
A context method that requires a message was called on a non-message update
Handlers that panic will crash the goroutine processing the update, taking down that update’s processing. The middleware.Recover middleware wraps every handler in a deferred recover:
// Recover returns a middleware that recovers a panic happened in// the handler.func Recover(onError ...RecoverFunc) tele.MiddlewareFunc
It captures both error-typed panics and string-typed panics, converting them to errors that are then routed through b.OnError (or a custom RecoverFunc).
b.Use(middleware.Recover())
Register Recover as the first middleware (outermost) with b.Use so it wraps the entire middleware chain. Middleware registered before Recover won’t be protected.
// Recommended orderingb.Use(middleware.Recover()) // outermost — catches panics from everything belowb.Use(middleware.Logger()) // logs all requestsb.Use(AuthMiddleware) // custom auth
The default handler only logs to stderr. In production, route errors to a structured logger or an error-tracking service (Sentry, Datadog, etc.).
Use Recover middleware
Panics in handlers should never crash the bot. Always register middleware.Recover() globally, before other middleware.
Match specific API errors
Don’t treat all API errors the same. Check for ErrBlockedByUser to clean up stale users, ErrMessageNotModified to suppress noise, and FloodError to implement backoff.
Return errors from handlers
Always return errors from handler functions instead of swallowing them with log.Println. This ensures they flow through OnError and can be centrally tracked.