When the gateway receives SIGTERM, runner.stop() stops the grammY polling
loop but does not abort the in-flight getUpdates HTTP request. That request
hangs for up to 30 seconds (the Telegram API timeout). If a new gateway
instance starts polling during that window, Telegram returns a 409 Conflict
error, causing message loss and requiring exponential backoff recovery.
This is especially problematic with service managers (launchd, systemd)
that restart the process immediately after SIGTERM.
Wire an AbortController into the fetch layer so every Telegram API request
(especially the long-polling getUpdates) aborts immediately on shutdown:
- bot.ts: Accept optional fetchAbortSignal in TelegramBotOptions; wrap
the grammY fetch with AbortSignal.any() to merge the shutdown signal.
- monitor.ts: Create a per-iteration AbortController, pass its signal to
createTelegramBot, and abort it from the SIGTERM handler, force-restart
path, and finally block.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When grammY's runner exceeds maxRetryTime during a network outage,
runner.task() resolves cleanly. Previously, the polling loop treated
this as an intentional stop and exited permanently — killing Telegram
polling for the lifetime of the gateway process.
Now the outer loop detects this case and restarts with exponential
backoff, so polling recovers once connectivity is restored.
Also bumps maxRetryTime from 5 minutes to 60 minutes so the runner
itself survives longer outages (e.g. scheduled internet downtime)
without needing the outer loop restart path.
* fix(security): default standalone servers to loopback bind (#4)
Change canvas host and telegram webhook default bind from 0.0.0.0
(all interfaces) to 127.0.0.1 (loopback only) to prevent unintended
network exposure when no explicit host is configured.
* fix: restore telegram webhook host override while keeping loopback defaults (openclaw#13184) thanks @davidrudduck
* style: format telegram docs after rebase (openclaw#13184) thanks @davidrudduck
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(telegram): handle Grammy HttpError network failures (#3815)
Grammy wraps fetch errors in an .error property (not .cause). Added .error
traversal to collectErrorCandidates in network-errors.ts.
Registered scoped unhandled rejection handler in monitorTelegramProvider
to catch network errors that escape the polling loop (e.g., from setMyCommands
during bot setup). Handler is unregistered when the provider stops.
* fix(telegram): address review feedback for Grammy HttpError handling
- Gate .error traversal on HttpError name to avoid widening search graph
- Use runtime logger instead of console.warn for consistency
- Add isGrammyHttpError check to scope unhandled rejection handler
- Consolidate isNetworkRelatedError into isRecoverableTelegramNetworkError
- Add 'timeout' to recoverable message snippets for full coverage
- Add bot.catch() to prevent unhandled rejections from middleware
- Add isRecoverableNetworkError() to retry on transient failures
- Add maxRetryTime and exponential backoff to grammY runner
- Global unhandled rejection handler now logs recoverable errors
instead of crashing (fetch failures, timeouts, connection resets)
Fixes crash loop when Telegram API is temporarily unreachable.
The Telegram webhook and monitor now accept and pass through accountId and config parameters, enabling routing and configuration per Telegram account. Tests have been updated to verify correct bot instantiation and DM routing based on accountId bindings.