openclaw - 💡(How to fix) Fix [Bug] Telegram sessionKey omits accountId — outbound delivery bypassed in embedded backend when multiple Telegram accounts share a chat

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…

Error Message

  1. Fail loudly when sessionId resolution falls back to unknown. Should emit ERROR with (channel, accountId, chatId, lookupKey, candidateSessionIds) instead of silently bypassing.

Root Cause

#75991 was closed by #79502 which addresses the symptom in the CLI backend path. The same symptom reproduces in the embedded backend path. After deep log analysis, the root cause is one level up from the backend split: the persisted sessionKey schema for the Telegram channel omits accountId (format: agent:<agent>:telegram:direct:<chatId>). When the gateway has multiple Telegram accounts and the same user chat-id appears on more than one of them, the outbound dispatcher cannot resolve which account the response belongs to, falls back to sessionId=unknown, and bypasses the entire lane enqueue / deliverOutboundPayloads pipeline. This affects every backend, not just CLI.

Fix Action

Fix / Workaround

#75991 was closed by #79502 which addresses the symptom in the CLI backend path. The same symptom reproduces in the embedded backend path. After deep log analysis, the root cause is one level up from the backend split: the persisted sessionKey schema for the Telegram channel omits accountId (format: agent:<agent>:telegram:direct:<chatId>). When the gateway has multiple Telegram accounts and the same user chat-id appears on more than one of them, the outbound dispatcher cannot resolve which account the response belongs to, falls back to sessionId=unknown, and bypasses the entire lane enqueue / deliverOutboundPayloads pipeline. This affects every backend, not just CLI.

  • Telegram inbound is logged correctly: Inbound message telegram:8486459972 -> @FerClaw_bot (direct, 6 chars).
  • An agent session is created and the trajectory grows (e.g. ~/.openclaw/agents/main/sessions/6591a3b6-...trajectory.jsonl reaches 4.9 MB).
  • No lane enqueue / lane dequeue / deliverOutboundPayloads / reply_dispatch / routeReply events appear in the log for that session — these strings are zero hits in the entire day's log file.
  • No sendMessage HTTP call is observed for the affected session.
  • The user receives nothing in Telegram.
  • Other accounts on the same gateway (e.g. biblio) work normally, and crons running through the same embedded agent deliver to Telegram by chat-id correctly.

When the outbound dispatcher tries to resolve (accountId, chatId) -> sessionId to call deliverOutboundPayloads, the lookup cannot disambiguate. It falls back to sessionId=unknown and the pipeline short-circuits — the exact "delivery pipeline bypassed entirely" pattern reported in #75991. PR #79502 patches this in the CLI backend; the embedded backend reproduces the same bypass because the bug is in the sessionKey schema, upstream of the backend selection.

Code Example

$ jq -c '{sessionKey, sessionId, accountId, channel, target}' \
  ~/.openclaw/agents/main/sessions/6591a3b6-...trajectory.jsonl
{"sessionKey":"agent:main:telegram:direct:8486459972",
 "sessionId":"6591a3b6-c3bf-4549-ad20-ce67479741bf",
 "accountId":null,
 "channel":null,
 "target":null}

---

{"sessionKey":"agent:main:cron:b98bbf54-8bbf-4912-9ea4-ec798770ce06:run:792aa6de-...",
 "sessionId":"792aa6de-...", ...}

---

work=[active=agent:main:telegram:direct:8486459972(processing/embedded_run,q=1,age=6s last=embedded_run:started)
 |agent:main:telegram:direct:8486459972:active-memory:2bc2fd62d3ed(processing/model_call,q=0,age=6s last=model_call:started)
 queued=agent:main:telegram:direct:8486459972(processing/embedded_run,q=1,age=6s last=embedded_run:started)]

---

head -1 ~/.openclaw/agents/main/sessions/<session-id>.trajectory.jsonl | \
  jq '{sessionKey, accountId}'

---

TOKEN=$(jq -r '.channels.telegram.accounts.<account>.botToken' ~/.openclaw/openclaw.json)
curl -s "https://api.telegram.org/bot${TOKEN}/getUpdates?offset=-1&timeout=0&allowed_updates=%5B%22message%22%5D"
launchctl kickstart -k gui/$UID/ai.openclaw.gateway
RAW_BUFFERClick to expand / collapse

TL;DR

#75991 was closed by #79502 which addresses the symptom in the CLI backend path. The same symptom reproduces in the embedded backend path. After deep log analysis, the root cause is one level up from the backend split: the persisted sessionKey schema for the Telegram channel omits accountId (format: agent:<agent>:telegram:direct:<chatId>). When the gateway has multiple Telegram accounts and the same user chat-id appears on more than one of them, the outbound dispatcher cannot resolve which account the response belongs to, falls back to sessionId=unknown, and bypasses the entire lane enqueue / deliverOutboundPayloads pipeline. This affects every backend, not just CLI.

Environment

  • OpenClaw version: 2026.5.12 (commit f066dd2), latest stable at the time of report
  • Installation: npm install -g [email protected]
  • Node: v22.22.1
  • OS: macOS 15 / Apple Silicon, running as user LaunchAgent
  • Agent runner: embedded (agents.defaults.runner = null, backend = null in openclaw.json, defaulting to embedded). Confirmed subsystem agent/embedded in logs, embedded_run_failover_decision events.
  • Gateway hosts four Telegram accounts simultaneously: default (@FerClaw_bot), biblio (@OpenClaw_biblio_bot), infra-monitor (@FGSIinfrastructureMon_bot), hermes (@FGSIhermes_bot). All four are reachable from the same end-user chat_id 8486459972.

Symptom

For one of the four accounts (the one most actively used in a group with another bot from this gateway), the bot stopped responding entirely. Specifically:

  • Telegram inbound is logged correctly: Inbound message telegram:8486459972 -> @FerClaw_bot (direct, 6 chars).
  • An agent session is created and the trajectory grows (e.g. ~/.openclaw/agents/main/sessions/6591a3b6-...trajectory.jsonl reaches 4.9 MB).
  • No lane enqueue / lane dequeue / deliverOutboundPayloads / reply_dispatch / routeReply events appear in the log for that session — these strings are zero hits in the entire day's log file.
  • No sendMessage HTTP call is observed for the affected session.
  • The user receives nothing in Telegram.
  • Other accounts on the same gateway (e.g. biblio) work normally, and crons running through the same embedded agent deliver to Telegram by chat-id correctly.

Root cause (verified)

Inspecting the persisted session records, the Telegram sessionKey schema omits accountId:

$ jq -c '{sessionKey, sessionId, accountId, channel, target}' \
  ~/.openclaw/agents/main/sessions/6591a3b6-...trajectory.jsonl
{"sessionKey":"agent:main:telegram:direct:8486459972",
 "sessionId":"6591a3b6-c3bf-4549-ad20-ce67479741bf",
 "accountId":null,
 "channel":null,
 "target":null}

For comparison, the cron sessionKey is well-formed:

{"sessionKey":"agent:main:cron:b98bbf54-8bbf-4912-9ea4-ec798770ce06:run:792aa6de-...",
 "sessionId":"792aa6de-...", ...}

The Telegram inbound log line carries the bot identity (-> @FerClaw_bot), so the information is available at ingress. It is lost on session creation: the sessionKey produced is agent:main:telegram:direct:<chatId> and the persisted accountId is null.

The runtime work queue confirms the same key shape:

work=[active=agent:main:telegram:direct:8486459972(processing/embedded_run,q=1,age=6s last=embedded_run:started)
 |agent:main:telegram:direct:8486459972:active-memory:2bc2fd62d3ed(processing/model_call,q=0,age=6s last=model_call:started)
 queued=agent:main:telegram:direct:8486459972(processing/embedded_run,q=1,age=6s last=embedded_run:started)]

When the outbound dispatcher tries to resolve (accountId, chatId) -> sessionId to call deliverOutboundPayloads, the lookup cannot disambiguate. It falls back to sessionId=unknown and the pipeline short-circuits — the exact "delivery pipeline bypassed entirely" pattern reported in #75991. PR #79502 patches this in the CLI backend; the embedded backend reproduces the same bypass because the bug is in the sessionKey schema, upstream of the backend selection.

Reproduction

  1. Install OpenClaw 2026.5.12, with agents.defaults.runner = null (embedded backend default).
  2. Configure two or more Telegram bots in channels.telegram.accounts.*, all enabled, all with the same admin user (same chat_id) in their respective allowFrom.
  3. Use both bots from that same chat_id. The first bot used gets its trajectory created with sessionKey: agent:main:telegram:direct:<chatId>. From that moment, inbound messages to the other bot generate an agent run but no lane enqueue / outbound delivery occurs.
  4. Verify with:
head -1 ~/.openclaw/agents/main/sessions/<session-id>.trajectory.jsonl | \
  jq '{sessionKey, accountId}'

Expect accountId: null and a sessionKey without the account discriminator.

Workarounds

A. Per-session allowed_updates narrowing — short-lived, also addresses parallel exotic-update-types crash:

TOKEN=$(jq -r '.channels.telegram.accounts.<account>.botToken' ~/.openclaw/openclaw.json)
curl -s "https://api.telegram.org/bot${TOKEN}/getUpdates?offset=-1&timeout=0&allowed_updates=%5B%22message%22%5D"
launchctl kickstart -k gui/$UID/ai.openclaw.gateway

Short-lived: worker re-registers the full 22-type list on the next getUpdates. (See #45289, #20786, #51916.)

B. Remove the bot from all groups — eliminates the upstream trigger. The sessionKey collision bug still exists but is masked in DM-only usage.

C. Downgrade to 2026.5.2 — NOT VIABLE. Config schema produced by 5.12 contains structures 5.2 cannot parse; gateway exits with EX_CONFIG (78) indefinitely. Tested with full clean reinstall.

Suggested fix

  1. Change the Telegram sessionKey schema to include accountId. Format: agent:<agent>:telegram:<accountId>:direct:<chatId>. Persist accountId in session metadata. This is the root fix and obsoletes backend-specific patches.

  2. Add a migration path for existing sessions. On gateway start, scan sessions matching agent:*:telegram:direct:* and either rewrite with inferred accountId or quarantine to sessions/legacy-pre-accountid/.

  3. Fail loudly when sessionId resolution falls back to unknown. Should emit ERROR with (channel, accountId, chatId, lookupKey, candidateSessionIds) instead of silently bypassing.

  4. Restore stderr capture in the LaunchAgent generator. Current installer sets StandardErrorPath = /dev/null, hiding all stack traces. Suggested default: ~/.openclaw/logs/gateway.err.log.

  5. (Parallel) Fix exotic update types parser. resolveTelegramAllowedUpdates() should not register types the parser doesn't handle. Suggested allowlist: ["message", "edited_message", "callback_query", "my_chat_member"]. (See #45289, #20786, #51916.)

Logs and artifacts available

  • Full openclaw.json with secrets redacted
  • Full getWebhookInfo for all four bots
  • Full daily log (~500 KB) covering the affected window
  • Sample trajectory.jsonl excerpts showing accountId: null
  • Diagnostic block with event_loop_delay warnings (eventLoopDelayMaxMs=1667.2 ms)

Happy to attach on request.

Related issues

  • #75991 — same symptom, closed by #79502 (CLI backend only)
  • #79502 — CLI backend fix; does not address embedded backend or sessionKey schema gap
  • #45289 — message_reaction updates not delivered (parallel bug)
  • #20786, #51916 — Telegram Business support; same parser area
  • #54529 — apiRoot updates received but not dispatched; similar bypass pattern

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