fix(telegram): prevent duplicate messages when preview edit times out#41662
Conversation
Greptile SummaryThis PR fixes a duplicate-message bug in the Telegram delivery pipeline that could occur when a slow proxy chain (e.g. OpenRouter via Cloudflare AI Gateway) caused
The change is well-reasoned and the two new integration tests in Key points to review:
Confidence Score: 4/5
Last reviewed commit: 8ba0127 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8ba0127c23
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f946659d58
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e2048676fe
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: db6b3fcfae
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
Thanks for digging into this and isolating the duplicate path. I pushed a small follow-up on your branch that keeps the narrow fix shape, but moves final edit classification into one place: pre-connect failures now fall back to a real send, ambiguous post-connect failures keep the existing preview, and |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e9f132acaf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8537211a1a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b1b607e699
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
cd036b3 to
9b17c64
Compare
…nal edit failures as delivered
9b17c64 to
2780e62
Compare
|
Merged via squash.
Thanks @hougangdev! |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2780e62d07
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return "preview-finalized"; | ||
| } | ||
| if (finalized === "retained") { | ||
| params.retainPreviewOnCleanupByLane.answer = true; |
There was a problem hiding this comment.
Clear stale retain flag before fallback final sends
Setting retainPreviewOnCleanupByLane.answer = true here makes the retain flag sticky across later final deliveries in the same dispatch. If a subsequent answer final cannot edit the active preview and falls back to sendPayload (for example a pre-connect edit failure), deliverLaneText never resets this flag, so the cleanup path in dispatchTelegramMessage (shouldClear check) skips stream.clear() and leaves the active partial preview visible next to the fallback-sent final message. This reintroduces duplicate/stale bubbles in multi-message streams.
Useful? React with 👍 / 👎.
…openclaw#41662) Merged via squash. Prepared head SHA: 2780e62 Co-authored-by: hougangdev <[email protected]> Co-authored-by: obviyus <[email protected]> Reviewed-by: @obviyus
…openclaw#41662) Merged via squash. Prepared head SHA: 2780e62 Co-authored-by: hougangdev <[email protected]> Co-authored-by: obviyus <[email protected]> Reviewed-by: @obviyus
…openclaw#41662) Merged via squash. Prepared head SHA: 2780e62 Co-authored-by: hougangdev <[email protected]> Co-authored-by: obviyus <[email protected]> Reviewed-by: @obviyus
…openclaw#41662) Merged via squash. Prepared head SHA: 2780e62 Co-authored-by: hougangdev <[email protected]> Co-authored-by: obviyus <[email protected]> Reviewed-by: @obviyus
…openclaw#41662) Merged via squash. Prepared head SHA: 2780e62 Co-authored-by: hougangdev <[email protected]> Co-authored-by: obviyus <[email protected]> Reviewed-by: @obviyus
…openclaw#41662) Merged via squash. Prepared head SHA: 2780e62 Co-authored-by: hougangdev <[email protected]> Co-authored-by: obviyus <[email protected]> Reviewed-by: @obviyus
…openclaw#41662) Merged via squash. Prepared head SHA: 2780e62 Co-authored-by: hougangdev <[email protected]> Co-authored-by: obviyus <[email protected]> Reviewed-by: @obviyus
…openclaw#41662) Merged via squash. Prepared head SHA: 2780e62 Co-authored-by: hougangdev <[email protected]> Co-authored-by: obviyus <[email protected]> Reviewed-by: @obviyus
…openclaw#41662) Merged via squash. Prepared head SHA: 2780e62 Co-authored-by: hougangdev <[email protected]> Co-authored-by: obviyus <[email protected]> Reviewed-by: @obviyus
…openclaw#41662) Merged via squash. Prepared head SHA: 2780e62 Co-authored-by: hougangdev <[email protected]> Co-authored-by: obviyus <[email protected]> Reviewed-by: @obviyus
Summary
Fixes the Telegram duplicate message bug that occurs when streaming preview edits time out — most commonly triggered by slow provider proxy chains like OpenRouter (via Cloudflare AI Gateway).
Root cause
When
editMessageTelegramthrows a post-connect network error (timeout, connection reset), the edit has likely already been processed by Telegram's server. However, the lane delivery fallback chain intryEditPreviewMessage(lane-delivery-text-deliverer.ts:217-220) treated all edit errors the same — returningfalseand falling through tosendPayload, which sends a second, duplicate message.Why OpenRouter triggers this
OpenRouter (especially routed through Cloudflare AI Gateway) adds significant latency to the LLM response path. While the LLM response itself isn't the issue, the added network hops and proxy overhead make the Telegram API calls themselves more likely to timeout. The sequence:
sendMessageeditMessageTexteditMessageTextto finalize the previeweditMessageTelegramretries (defaultTELEGRAM_RETRY_REmatches "timeout")tryEditPreviewMessagetreatEditFailureAsDeliveredisfalsefor normal previews → returnsfalsesendPayloadfires → user sees two identical messagesThe same bug can occur without OpenRouter on any high-latency or unstable connection to Telegram's API.
Fix
The
editPreviewcallback inbot-message-dispatch.tsnow classifies errors before propagating:isSafeToRetrySendError)ECONNREFUSED,ENOTFOUNDtimeout,reset,fetch failed400,500Relationship to #40883
#40883 addresses the same symptom via a broader structural refactor (~500 lines across 7 files): it removes the
archivedAnswerPreviewssystem entirely, coalesces answer segments into a single preview, and flipstreatEditFailureAsDeliveredtotruefor all final edits.This PR is a surgical alternative (3 files, ~135 lines) that fixes the root cause at the network-error classification layer — the
editPreviewcallback distinguishes pre-connect errors (safe to retry/fallback) from post-connect errors (edit likely landed, swallow to avoid duplicate). Both PRs prevent the duplicate; this one is narrower in scope and preserves the existing lane delivery architecture.Related issues and PRs
Test plan
pnpm vitest run src/telegram/lane-delivery.test.ts— 16 tests passpnpm vitest run src/telegram/bot-message-dispatch.test.ts— 61 tests pass (2 new tests for the fix)pnpm tsgo— no type errors🤖 Generated with Claude Code