You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Behavior bug (incorrect output/state without crash)
Beta release blocker
Yes
Summary
Telegram isolated polling has replay/recovery paths, but handleInboundMessageLike() does not persist a per-message dispatch idempotency key before handing the message to the agent, so a replayed Telegram message_id can dispatch a second agent turn after restart/recovery.
Steps to reproduce
NOT_ENOUGH_INFO
The source-level risk is clear, but I do not have a short deterministic repro script yet. The observed local incident involved Telegram polling restarts/recovery while a DM turn was draining; this issue is filed to track the missing durable guard separately from the visible draft-stream spam in #84885.
Expected behavior
For Telegram message-like updates, OpenClaw should process a stable Telegram message identity at most once per account/chat/message id across gateway restarts, isolated-polling offset replay, stale-claim recovery, and process recreation.
A replayed Telegram update should be skipped before it calls the agent runner if the same account/chat/message id was already dispatched successfully or is already in flight.
Actual behavior
handleInboundMessageLike() currently checks in-memory/update-level skip state, then records the message in the reply-chain cache and dispatches it to processInboundMessage(). The reply-chain cache is not a dispatch idempotency ledger.
Webhook+polling double delivery was ruled out locally: Telegram getWebhookInfo had an empty webhook URL and no pending updates.
Logs, screenshots, and evidence
flowchart TD
A[Telegram getUpdates update_id N] --> B[OpenClaw isolated polling/spool]
B --> C[handleInboundMessageLike]
C --> D[record reply-chain cache]
D --> E[processInboundMessage / agent run]
B --> F[restart or recovery replays update_id N]
F --> C
G[Missing durable guard: accountId + chatId + message_id] -. should be checked before .-> E
classDef bad fill:#ffd6d6,stroke:#cc3333,color:#111;
class G bad;
Loading
The existing repo already has persistent/claimable dedupe primitives used by other channels:
Telegram should use the same kind of durable/claimable guard for message-like updates after authorization and before recordMessageForReplyChain() / processInboundMessage().
Impact and severity
Affected: Telegram users during polling replay/recovery/restart windows.
Severity: High / beta blocker when combined with partial streaming, because one user message can fan out into repeated agent work and visible repeated replies.
Frequency: Intermittent; depends on restart/recovery timing.
Consequence: duplicate agent runs, duplicate replies, extra model/API cost, confusing Telegram state.
Additional information
Related issues, but this is a distinct missing guard:
Suggested fix shape: add a Telegram message-level claimable/persistent dedupe guard keyed by accountId, chatId, and Telegram message_id; claim after auth/policy checks; commit after dispatch ownership is established; release only for retryable pre-dispatch errors.
Bug type
Behavior bug (incorrect output/state without crash)
Beta release blocker
Yes
Summary
Telegram isolated polling has replay/recovery paths, but
handleInboundMessageLike()does not persist a per-message dispatch idempotency key before handing the message to the agent, so a replayed Telegrammessage_idcan dispatch a second agent turn after restart/recovery.Steps to reproduce
NOT_ENOUGH_INFO
The source-level risk is clear, but I do not have a short deterministic repro script yet. The observed local incident involved Telegram polling restarts/recovery while a DM turn was draining; this issue is filed to track the missing durable guard separately from the visible draft-stream spam in #84885.
Expected behavior
For Telegram message-like updates, OpenClaw should process a stable Telegram message identity at most once per account/chat/message id across gateway restarts, isolated-polling offset replay, stale-claim recovery, and process recreation.
A replayed Telegram update should be skipped before it calls the agent runner if the same account/chat/message id was already dispatched successfully or is already in flight.
Actual behavior
handleInboundMessageLike()currently checks in-memory/update-level skip state, then records the message in the reply-chain cache and dispatches it toprocessInboundMessage(). The reply-chain cache is not a dispatch idempotency ledger.Relevant path:
Polling/recovery paths are intentionally at-least-once around restart/claim recovery:
No durable
message:${accountId}:${chatId}:${message_id}dispatch ledger is checked between successful auth/policy checks andprocessInboundMessage().OpenClaw version
2026.5.19
Operating system
macOS 26 / Darwin arm64
Install method
Global OpenClaw gateway managed by launchd (
ai.openclaw.gateway)Model
OpenRouter/OpenAI GPT-5 family route in the affected session; exact final model for every dispatch is NOT_ENOUGH_INFO.
Provider / routing chain
Telegram polling -> OpenClaw gateway -> embedded agent -> OpenRouter/OpenAI route
Additional provider/model setup details
Local Telegram status after mitigation:
{ "mode": "polling", "running": true, "connected": true, "webhook_url": "", "pending_update_count": 0 }Webhook+polling double delivery was ruled out locally: Telegram
getWebhookInfohad an empty webhook URL and no pending updates.Logs, screenshots, and evidence
The existing repo already has persistent/claimable dedupe primitives used by other channels:
Telegram should use the same kind of durable/claimable guard for message-like updates after authorization and before
recordMessageForReplyChain()/processInboundMessage().Impact and severity
Affected: Telegram users during polling replay/recovery/restart windows.
Severity: High / beta blocker when combined with partial streaming, because one user message can fan out into repeated agent work and visible repeated replies.
Frequency: Intermittent; depends on restart/recovery timing.
Consequence: duplicate agent runs, duplicate replies, extra model/API cost, confusing Telegram state.
Additional information
Related issues, but this is a distinct missing guard:
.processingclaims surviving gateway recreate.Suggested fix shape: add a Telegram message-level claimable/persistent dedupe guard keyed by
accountId,chatId, and Telegrammessage_id; claim after auth/policy checks; commit after dispatch ownership is established; release only for retryable pre-dispatch errors.