openclaw - 💡(How to fix) Fix [Bug]: Isolated polling ingress never calls bot.init() — Telegram channel halts with "Bot not initialized"

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

When a Telegram account uses isolated polling ingress, the spool drain calls bot.handleUpdate(update) on a brand-new grammY Bot that was never initialized. Every spooled update fails with Bot not initialized!, the spool grows, and the channel stops responding to all messages — gateway restarts do not recover it.

Error Message

Call bot.init() once at the top of #runIsolatedIngressCycle, with the same recoverable-error retry pattern used for #ensureWebhookCleanup. bot.init() is idempotent in grammY, so this is safe even if botInfo was supplied.

Root Cause

In monitor-polling.runtime (the file shipped as dist/monitor-polling.runtime-*.js):

  • #runPollingCycle(bot) uses grammY's run(bot, runner) which internally calls bot.init() on the first cycle.
  • #runIsolatedIngressCycle(bot) bypasses run() entirely — it polls via a separate worker that writes to a spool directory and drains by calling bot.handleUpdate(update) directly.
  • The grammY Bot constructor only treats the bot as pre-initialized when botInfo is passed (bot-*.js, in createTelegramBotCore, where botConfig only includes botInfo if opts.botInfo is provided). In #createPollingBot, botInfo is taken from this.opts.botInfo, which is typically not supplied — so grammY throws Bot not initialized on the first handleUpdate call.

Net effect: both possible init paths (explicit bot.init() and constructor-supplied botInfo) are missing in isolated-ingress mode, so the bot is permanently in an uninitialized state for the lifetime of that polling cycle.

Fix Action

Fix / Workaround

Applied the patch locally to the dist file and restarted the gateway:

  • Before: 570 Bot not initialized errors per day, 5 spool directories accumulating files.
  • After: 0 init errors across multiple clean restarts, all 5 spools drain to 0 items, inbound messages dispatched through middleware normally.

Code Example

[telegram][diag] spooled update <id> handler failed; keeping for retry:
  Bot not initialized! Either call `await bot.init()`, or directly set
  the `botInfo` option in the `Bot` constructor to specify a known bot info object.

---

async #runIsolatedIngressCycle(bot) {
     const ingress = this.opts.isolatedIngress;
     if (!ingress?.enabled) return this.#runPollingCycle(bot);
+    if (!bot.botInfo) try {
+        await bot.init();
+    } catch (err) {
+        return await this.#waitBeforeRetryOnRecoverableSetupError(err, "Telegram bot init failed") ? "continue" : "exit";
+    }
     const spoolDir = ingress.spoolDir ?? resolveTelegramIngressSpoolDir({ accountId: this.opts.accountId });
RAW_BUFFERClick to expand / collapse

Summary

When a Telegram account uses isolated polling ingress, the spool drain calls bot.handleUpdate(update) on a brand-new grammY Bot that was never initialized. Every spooled update fails with Bot not initialized!, the spool grows, and the channel stops responding to all messages — gateway restarts do not recover it.

Version

  • openclaw 2026.5.12
  • Node 25.9.0
  • macOS Darwin 25.4.0

Symptom

After gateway start, the polling worker successfully fetches updates and writes them to ~/.openclaw/telegram/ingress-spool-{account}/. The 500ms drain timer then floods the log with:

[telegram][diag] spooled update <id> handler failed; keeping for retry:
  Bot not initialized! Either call `await bot.init()`, or directly set
  the `botInfo` option in the `Bot` constructor to specify a known bot info object.

No replies are ever sent. With 5 Telegram accounts configured I observed 570 occurrences in a single log day across all ingress-spool-* directories. Restarting the gateway does not help — fresh bots created after restart hit the same code path and fail identically.

Reproduction

  1. Configure ≥1 Telegram account with isolated polling ingress enabled.
  2. Restart the gateway (openclaw gateway restart).
  3. Send any direct message to the bot from Telegram.
  4. Tail /tmp/openclaw/openclaw-*.logspooled update ... handler failed errors appear every ~500ms.
  5. Spool files in ~/.openclaw/telegram/ingress-spool-{account}/ accumulate and are never deleted.

Root cause

In monitor-polling.runtime (the file shipped as dist/monitor-polling.runtime-*.js):

  • #runPollingCycle(bot) uses grammY's run(bot, runner) which internally calls bot.init() on the first cycle.
  • #runIsolatedIngressCycle(bot) bypasses run() entirely — it polls via a separate worker that writes to a spool directory and drains by calling bot.handleUpdate(update) directly.
  • The grammY Bot constructor only treats the bot as pre-initialized when botInfo is passed (bot-*.js, in createTelegramBotCore, where botConfig only includes botInfo if opts.botInfo is provided). In #createPollingBot, botInfo is taken from this.opts.botInfo, which is typically not supplied — so grammY throws Bot not initialized on the first handleUpdate call.

Net effect: both possible init paths (explicit bot.init() and constructor-supplied botInfo) are missing in isolated-ingress mode, so the bot is permanently in an uninitialized state for the lifetime of that polling cycle.

Suggested fix

Call bot.init() once at the top of #runIsolatedIngressCycle, with the same recoverable-error retry pattern used for #ensureWebhookCleanup. bot.init() is idempotent in grammY, so this is safe even if botInfo was supplied.

 async #runIsolatedIngressCycle(bot) {
     const ingress = this.opts.isolatedIngress;
     if (!ingress?.enabled) return this.#runPollingCycle(bot);
+    if (!bot.botInfo) try {
+        await bot.init();
+    } catch (err) {
+        return await this.#waitBeforeRetryOnRecoverableSetupError(err, "Telegram bot init failed") ? "continue" : "exit";
+    }
     const spoolDir = ingress.spoolDir ?? resolveTelegramIngressSpoolDir({ accountId: this.opts.accountId });

Verification of fix

Applied the patch locally to the dist file and restarted the gateway:

  • Before: 570 Bot not initialized errors per day, 5 spool directories accumulating files.
  • After: 0 init errors across multiple clean restarts, all 5 spools drain to 0 items, inbound messages dispatched through middleware normally.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix [Bug]: Isolated polling ingress never calls bot.init() — Telegram channel halts with "Bot not initialized"