fix(telegram): use idempotent retry context for delete/reaction#96612
Conversation
reactMessageTelegram and deleteMessageTelegram passed context: "send" to isRecoverableTelegramNetworkError, which disables message-snippet matching (allowMessageMatch defaults to false only for "send"). Both operations are idempotent (setMessageReaction / deleteMessage are safe to repeat), yet a transient snippet-only network error (e.g. "socket hang up", "undici network error" with no error code) was not retried — stricter than polling/webhook/ unknown, which all default allowMessageMatch to true. Users saw spurious reaction/delete failures on transient network errors. Add delete | react to TelegramNetworkErrorContext (additive) and use them at the two callers. The helper default (context !== "send") is unchanged, so delete/react now match polling/webhook/unknown. sendMessage keeps "send". Co-Authored-By: Claude <[email protected]>
|
Codex review: needs maintainer review before merge. Reviewed June 24, 2026, 9:01 PM ET / 01:01 UTC. Summary PR surface: Source +6, Tests +10. Total +16 across 3 files. Reproducibility: yes. at source level: current main passes Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Next step before merge
Security Review detailsBest possible solution: Land the focused retry-context fix after relevant CI completes, while preserving the strict safe-send policy for non-idempotent Telegram sends. Do we have a high-confidence way to reproduce the issue? Yes, at source level: current main passes Is this the best way to solve the issue? Yes; separate AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 8a5cb85c31e5. Label changesLabel justifications:
Evidence reviewedPR surface: Source +6, Tests +10. Total +16 across 3 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
…claw#96612) reactMessageTelegram and deleteMessageTelegram passed context: "send" to isRecoverableTelegramNetworkError, which disables message-snippet matching (allowMessageMatch defaults to false only for "send"). Both operations are idempotent (setMessageReaction / deleteMessage are safe to repeat), yet a transient snippet-only network error (e.g. "socket hang up", "undici network error" with no error code) was not retried — stricter than polling/webhook/ unknown, which all default allowMessageMatch to true. Users saw spurious reaction/delete failures on transient network errors. Add delete | react to TelegramNetworkErrorContext (additive) and use them at the two callers. The helper default (context !== "send") is unchanged, so delete/react now match polling/webhook/unknown. sendMessage keeps "send". Co-authored-by: Claude <[email protected]>
…claw#96612) reactMessageTelegram and deleteMessageTelegram passed context: "send" to isRecoverableTelegramNetworkError, which disables message-snippet matching (allowMessageMatch defaults to false only for "send"). Both operations are idempotent (setMessageReaction / deleteMessage are safe to repeat), yet a transient snippet-only network error (e.g. "socket hang up", "undici network error" with no error code) was not retried — stricter than polling/webhook/ unknown, which all default allowMessageMatch to true. Users saw spurious reaction/delete failures on transient network errors. Add delete | react to TelegramNetworkErrorContext (additive) and use them at the two callers. The helper default (context !== "send") is unchanged, so delete/react now match polling/webhook/unknown. sendMessage keeps "send". Co-authored-by: Claude <[email protected]>
…claw#96612) reactMessageTelegram and deleteMessageTelegram passed context: "send" to isRecoverableTelegramNetworkError, which disables message-snippet matching (allowMessageMatch defaults to false only for "send"). Both operations are idempotent (setMessageReaction / deleteMessage are safe to repeat), yet a transient snippet-only network error (e.g. "socket hang up", "undici network error" with no error code) was not retried — stricter than polling/webhook/ unknown, which all default allowMessageMatch to true. Users saw spurious reaction/delete failures on transient network errors. Add delete | react to TelegramNetworkErrorContext (additive) and use them at the two callers. The helper default (context !== "send") is unchanged, so delete/react now match polling/webhook/unknown. sendMessage keeps "send". Co-authored-by: Claude <[email protected]>
…claw#96612) reactMessageTelegram and deleteMessageTelegram passed context: "send" to isRecoverableTelegramNetworkError, which disables message-snippet matching (allowMessageMatch defaults to false only for "send"). Both operations are idempotent (setMessageReaction / deleteMessage are safe to repeat), yet a transient snippet-only network error (e.g. "socket hang up", "undici network error" with no error code) was not retried — stricter than polling/webhook/ unknown, which all default allowMessageMatch to true. Users saw spurious reaction/delete failures on transient network errors. Add delete | react to TelegramNetworkErrorContext (additive) and use them at the two callers. The helper default (context !== "send") is unchanged, so delete/react now match polling/webhook/unknown. sendMessage keeps "send". Co-authored-by: Claude <[email protected]> (cherry picked from commit ce15f34)
…claw#96612) reactMessageTelegram and deleteMessageTelegram passed context: "send" to isRecoverableTelegramNetworkError, which disables message-snippet matching (allowMessageMatch defaults to false only for "send"). Both operations are idempotent (setMessageReaction / deleteMessage are safe to repeat), yet a transient snippet-only network error (e.g. "socket hang up", "undici network error" with no error code) was not retried — stricter than polling/webhook/ unknown, which all default allowMessageMatch to true. Users saw spurious reaction/delete failures on transient network errors. Add delete | react to TelegramNetworkErrorContext (additive) and use them at the two callers. The helper default (context !== "send") is unchanged, so delete/react now match polling/webhook/unknown. sendMessage keeps "send". Co-authored-by: Claude <[email protected]>
What Problem This Solves
reactMessageTelegramanddeleteMessageTelegram(extensions/telegram/src/send.ts) passedcontext: "send"toisRecoverableTelegramNetworkError. The "send" context is the only one that defaultsallowMessageMatchtofalse, disabling message-snippet matching. Both operations are idempotent (api.setMessageReaction/api.deleteMessageare safe to repeat), so a transient error recognizable only by message snippet (e.g.socket hang up,undici network errorwith no errorcode) was NOT retried — stricter than every sibling context (polling,webhook,unknownall defaultallowMessageMatchto true). Users saw spurious reaction/delete failures on transient network blips.Why This Change Was Made
isRecoverableTelegramNetworkError'scontextencodes whether the caller is a non-idempotent write; only"send"opts into the strict path (options.context !== "send"at network-errors.ts:309). Grouping idempotent delete/reaction under "send" is a classification error. The existing test "skips broad message matches for send context" already locks the send-vs-polling contract, confirming the semantics are intentional — delete/react were simply mislabeled.User Impact
Transient network errors during emoji reactions (approval bubbles, user-feedback reactions) and message cleanup deletes now retry like polling instead of failing immediately. sendMessage behavior is unchanged (still strict, no duplicate-message risk).
Real behavior proof
Behavior addressed:
reactMessageTelegram/deleteMessageTelegramusedcontext: "send", so transient snippet-only network errors were not retried even though both operations are idempotent.Real environment tested: Linux x86_64, Node 22, commit
a85f1d59c3on branchfix/telegram-idempotent-retry-context.Exact steps or command run after this patch:
node --import tsx /tmp/verify-telegram-retry-context.mjs— imports the realisRecoverableTelegramNetworkErrorand checks snippet-only transient errors across retry contexts.Evidence after fix: Terminal capture of
noderuntime verification (copied live output):Observed result after fix: For snippet-only transient errors,
react/deletenow return recoverable (retry), matchingpolling;sendstays non-recoverable.What was not tested: Live end-to-end against a real Telegram bot under an injected transient socket error (function-level proof covers the retry predicate directly);
sendChatActionalso usescontext: "send"but is out of scope here.Tests and validation
Added one regression case to
network-errors.test.ts: delete/react contexts treat a snippet-only error as recoverable (like polling), while send stays strict.Risk checklist
Closes: N/A (no existing issue)