If you run a self-hosted Telegram bot and have ever watched it go quiet for ten minutes for no obvious reason, the cause is almost always the same: long-poll ingress sharing the same event loop as everything else your runtime is doing. When the loop stalls, getUpdates stalls, and Telegram has no way to tell you. Outbound sends keep working, which makes the failure even harder to spot.

v2026.5.12 (released 2026-05-14) ships the fix as PR #81746, closing the long-standing issue #81132. Telegram ingress now runs in an isolated worker with a durable local spool, so polling survives main-loop stalls and updates are not lost between transport rebuilds.

Here is what changed, why it matters for self-hosted AI agents, and what to verify after upgrading.

What “self-hosted Telegram bot polling” actually does

The Telegram Bot API offers two delivery modes: webhooks (Telegram pushes to your URL) and long polling (your bot calls getUpdates and the call hangs until there is something to return or a timeout fires). Almost every self-hosted bot uses long polling because it does not need a public HTTPS endpoint, a static IP, or a TLS certificate Telegram trusts.

Long polling has a hidden contract: you must keep calling getUpdates continuously, ack each batch with the next offset, and never lose more than a few seconds between calls. If you go quiet for too long, Telegram’s API server eventually drops your slot. Worse, while you are quiet, new messages pile up on Telegram’s side and nothing on your end notices.

For an AI agent runtime that is also handling LLM streams, plugin installs, voice transcription, and tool execution, “going quiet for a few seconds” is not theoretical. A long context-engine summarization, a slow plugin install, or a single misbehaving model stream can stall the loop just enough to cause polling to silently fall behind.

What broke before v2026.5.12

The pre-2026.5.12 ingress lived on the same Node event loop as the rest of . When that loop got busy:

SymptomWhat was actually happening
Bot stops responding for minutes, then resumesgetUpdates was starved; Telegram queued updates server-side until polling caught up
Bot resumes but skips a chunk of messagesThe catch-up loop missed updates older than Telegram’s retention window
Outbound sendMessage works while inbound is deadOutbound API calls were independent of polling and kept succeeding, masking the dead inbound side
getUpdates returns 409 after restartToken rotation left a stale long-poll offset that could not be tied to the current bot token

The 409 case was filed as #80671 and the silent-stall case as #81132. Both shipped fixes in v2026.5.12.

What v2026.5.12 changes

Three coordinated changes, all in the v2026.5.12 release notes:

1. Isolated polling worker. Telegram ingress now runs in its own worker, separate from the main agent runtime. A slow LLM call, a long context-engine summarization, or a busy plugin installer no longer starves getUpdates. The worker’s only job is to keep polling and write incoming updates to the local spool.

2. Durable local spool. Updates are written to disk before they are handed to the agent. If the runtime crashes or restarts mid-batch, the spool replays on boot. Before this change, an in-flight update on a crash was just gone.

3. Polling-stall detection from getUpdates liveness only. This is subtle. Outbound API calls were previously counted as “the bot is alive” signals, which is why dead inbound polling could go unnoticed for so long. v2026.5.12 detects polling stalls only from inbound-call liveness, and emits a polling-cycle-start log line after each transport rebuild. If your inbound channel dies, you now see it in the logs immediately. (This is the #78473 fix.)

There is also a related fix for token rotation: stale long-poll offsets that cannot be matched to the current bot token are now discarded (#80671), so rotating a bot token no longer leaves the bot silently skipping new messages on the next start.

Why this matters more for AI agent runtimes than for plain bots

A purpose-built Telegram bot framework like python-telegram-bot or grammY can keep its event loop fairly clean — its job is mostly Telegram. An AI agent runtime cannot. It is also running inference, vector search, voice transcoding, browser automation, plugin installs, and whatever tools the model decided to call this turn. Any one of those can block the loop for seconds.

The old “share the loop with everything else” design was acceptable for tiny bots. It was not acceptable for self-hosted AI agents that also happen to talk to Telegram. Isolating ingress is a real architectural change, not a one-line patch.

A self-hosted AI agent platform that depends on chat-channel reliability (most of them, including ) needs ingress isolation by default. If your platform does not have it, polling-stall outages are inevitable as soon as your agent does anything non-trivial.

What to verify after upgrading

After you upgrade to 2026.5.12 (openclaw update or npm i -g @openclaw/):

  1. Watch your logs for the new polling-cycle-start log line after a transport rebuild. If you see it, the worker is running.
  2. Force a long-running operation (a slow LLM stream, a large plugin install) and send a Telegram message during it. The message should arrive without delay, not after the operation finishes.
  3. If you have multi-account Telegram setups, double-check that group routing still behaves the way you want. v2026.5.12 also lands the #81030 fix for the explicit-empty accounts.<id>.groups: {} case under default groupPolicy: "allowlist". The TL;DR: an explicit-empty per-account groups map now falls back to the root channels.telegram.groups allowlist instead of dropping every group update silently.
  4. If you rotate a bot token, expect the next start to begin from a clean offset. No more silent skips.

If you also run Discord voice capture, the v2026.5.7 captureSilenceGraceMs fix is worth re-reading: same pattern of a chat-channel reliability fix landing behind a quietly-named release. Channel reliability work is currently the dominant theme across recent releases.

How this fits the broader v2026.5.12 picture

The Telegram ingress overhaul is the headline reliability change, but it is not the only one. v2026.5.12 also:

  • Externalizes the WhatsApp, Slack, Anthropic Vertex, and Amazon Bedrock plugin dependency cones, so a core install only pulls what you actually use.
  • Tightens sandbox path policy on Windows by adding USERPROFILE to the blocked home roots, so credential-bearing binds (.codex, .openclaw, .ssh) are denied even when HOME points elsewhere.
  • Keeps oauthRef-backed Codex OAuth profiles usable and stops high-confidence app-server OAuth refresh invalidation from retry-spamming raw token-refresh errors.
  • Adds an acp.fallbacks config so ACP turns can try configured backup runtime backends before any output is emitted.

These are mostly invisible until you hit one of them, which is the nature of self-hosted reliability work. To learn more about how the wider runtime fits together, see how works and why exists in the first place.

FAQ

How do I check what polling mode my bot is using?

If you have not configured a public webhook URL in your Telegram channel config, is using long polling. That is the default and the case the v2026.5.12 fix targets.

Do I need to change any config to get the new isolated worker?

No. The isolated polling worker is the new default in v2026.5.12. There is no opt-in flag.

What if I have a bot built on a different framework?

The fix is internal to . If you are running a self-hosted bot on a different framework, the same architectural pattern applies: run polling on its own worker, persist the spool, and detect liveness from inbound calls only. The implementation is yours; the principle is the same.

Are webhooks safer than long polling for self-hosted AI agents?

In theory yes, because Telegram pushes to you and there is no polling loop to stall. In practice, webhooks need a public HTTPS endpoint with a TLS certificate Telegram trusts, which is a real operational cost for a self-hosted setup behind a residential connection. Long polling with proper isolation is the more practical default. Read what is for context on the self-hosted-first design.

Putting v2026.5.12 to work

Upgrade to v2026.5.12, watch for the new polling-cycle-start log line, and test with a long-running operation as described above. If your bot was previously dropping messages during busy periods, this is the release that fixes it.

Sources: