Bug type
Behavior bug / stale turn replay after context-overflow auto-compaction in Telegram channel session.
Summary
A Telegram direct agent session hit context overflow during an active turn. OpenClaw auto-compacted successfully and logged retrying prompt, but the same inbound Telegram message was appended into the compacted transcript a second time with the same external message_id. After that, later user messages arrived while the stale/retried turn continued, and the assistant delivered replies for older exchanges after the user had moved on.
This looks related to the existing compaction/replay/queued-turn family, but the observed failure is specific:
- channel-backed session
- context-overflow during tool-heavy active turn
- auto-compaction succeeds
- retry uses the original inbound/current-message metadata again
- compacted transcript receives a duplicate user turn for the same external message id
- stale assistant output is delivered after newer Telegram messages
Environment
- OpenClaw:
2026.5.2 (8b2a6e5)
- Install: npm global
- OS: Linux x64
- Runtime: Gateway service via systemd user unit
- Channel: Telegram direct session
- Provider/model:
openai-codex/gpt-5.5
- Queue mode observed in status:
steer
Evidence
Transcript excerpt from a Telegram direct session, redacted to avoid exposing private chat identifiers:
20:40:25.113 user <plugin warning question>
20:40:25.117 runtime message_id="51024"
20:40:40.022 assistant starts handling that question
20:40:46.498 gateway context-overflow-diag ... error=Context overflow: estimated context size exceeds safe threshold during tool loop
20:40:46.498 gateway context overflow detected ... attempting auto-compaction
20:41:08.065 gateway auto-compaction succeeded ... retrying prompt
20:41:08.644 user <same plugin warning question again>
20:41:08.647 runtime message_id="51024" <-- same external Telegram message id appended again
20:42:59.643 user <newer message, message_id="51027">
20:58:38.744 user <newer message, message_id="51029">
20:59:57.936 user "You are still responding to old message exchanges", message_id="51031"
21:00:04.214 assistant acknowledges stale old-turn behavior
The duplicate was not a new Telegram inbound from the user: the transcript contains the same external Telegram message_id (51024) twice, before and after compaction retry.
Gateway log around the duplicate:
[agent/embedded] [context-overflow-diag] sessionKey=<telegram direct session> provider=openai-codex/gpt-5.5 source=assistantError messages=251 sessionFile=<session jsonl> diagId=ovf-... compactionAttempts=0 observedTokens=unknown error=Context overflow: estimated context size exceeds safe threshold during tool loop.
[agent/embedded] context overflow detected (attempt 1/3); attempting auto-compaction for openai-codex/gpt-5.5
[agent/embedded] auto-compaction succeeded for openai-codex/gpt-5.5; retrying prompt
Expected behavior
After auto-compaction succeeds, retry should continue the in-flight turn without appending the same channel inbound message/current message metadata as a fresh user turn. Channel-backed external message ids should be idempotent across compaction retries.
If a retry cannot safely preserve in-flight state, the runtime should fail/suppress/manual-recover rather than deliver stale output for an older external message after newer Telegram messages have arrived.
Actual behavior
The compacted transcript re-appended the same user content and same Telegram message_id, then older-turn assistant output continued after newer user messages were already present.
Suspected root cause
In the embedded PI retry loop, after context-overflow auto-compaction:
adoptCompactionTranscript() updates activeSessionId / activeSessionFile
- the loop logs
auto-compaction succeeded ... retrying prompt
- the next
runEmbeddedAttemptWithBackend(...) still receives the original:
prompt: params.prompt
transcriptPrompt: params.transcriptPrompt
currentMessageId: params.currentMessageId
sessionFile: activeSessionFile
The retry therefore appears to submit the same inbound prompt/current-message context into the new compacted transcript as though it were a fresh channel message.
A likely fix is one of:
- Make overflow/compaction retry idempotent for channel-backed prompts by detecting an already-recorded external
currentMessageId in the active transcript and switching the retry to a continuation prompt instead of re-submitting the original inbound prompt.
- Add a per-session/per-message generation fence so stale channel replies from superseded runs are suppressed if newer external message ids have arrived.
- Treat channel-backed compaction retry like restart recovery: fail/manual-recover instead of hidden replay when external delivery state cannot be guaranteed.
Related existing work
This appears related but not identical to:
Minimal diagnostic assertion that would catch this
For channel-backed sessions, after compaction/retry, the same external currentMessageId should not produce two role=user transcript entries unless the second inbound was actually received again from the channel transport.
Regression test idea:
- Create a channel-backed session with
currentMessageId = X.
- Force context-overflow auto-compaction during the active turn.
- Retry after compaction.
- Assert transcript contains only one user/runtime-context pair for external message id
X.
- Inject a newer message id
Y while old run is still active/finishing.
- Assert any final output from the old run is suppressed or explicitly fenced if it would be delivered after
Y.
Bug type
Behavior bug / stale turn replay after context-overflow auto-compaction in Telegram channel session.
Summary
A Telegram direct agent session hit context overflow during an active turn. OpenClaw auto-compacted successfully and logged
retrying prompt, but the same inbound Telegram message was appended into the compacted transcript a second time with the same externalmessage_id. After that, later user messages arrived while the stale/retried turn continued, and the assistant delivered replies for older exchanges after the user had moved on.This looks related to the existing compaction/replay/queued-turn family, but the observed failure is specific:
Environment
2026.5.2(8b2a6e5)openai-codex/gpt-5.5steerEvidence
Transcript excerpt from a Telegram direct session, redacted to avoid exposing private chat identifiers:
The duplicate was not a new Telegram inbound from the user: the transcript contains the same external Telegram
message_id(51024) twice, before and after compaction retry.Gateway log around the duplicate:
Expected behavior
After auto-compaction succeeds, retry should continue the in-flight turn without appending the same channel inbound message/current message metadata as a fresh user turn. Channel-backed external message ids should be idempotent across compaction retries.
If a retry cannot safely preserve in-flight state, the runtime should fail/suppress/manual-recover rather than deliver stale output for an older external message after newer Telegram messages have arrived.
Actual behavior
The compacted transcript re-appended the same user content and same Telegram
message_id, then older-turn assistant output continued after newer user messages were already present.Suspected root cause
In the embedded PI retry loop, after context-overflow auto-compaction:
adoptCompactionTranscript()updatesactiveSessionId/activeSessionFileauto-compaction succeeded ... retrying promptrunEmbeddedAttemptWithBackend(...)still receives the original:prompt: params.prompttranscriptPrompt: params.transcriptPromptcurrentMessageId: params.currentMessageIdsessionFile: activeSessionFileThe retry therefore appears to submit the same inbound prompt/current-message context into the new compacted transcript as though it were a fresh channel message.
A likely fix is one of:
currentMessageIdin the active transcript and switching the retry to a continuation prompt instead of re-submitting the original inbound prompt.Related existing work
This appears related but not identical to:
Minimal diagnostic assertion that would catch this
For channel-backed sessions, after compaction/retry, the same external
currentMessageIdshould not produce tworole=usertranscript entries unless the second inbound was actually received again from the channel transport.Regression test idea:
currentMessageId = X.X.Ywhile old run is still active/finishing.Y.