fix(gateway): skip queued-follow-up re-send when stream consumer already delivered - #56092
liuhao1024 wants to merge 1 commit into
Conversation
…ady delivered When the stream consumer sends the response but the stream task is cancelled before setting _final_response_sent (e.g. image delivery or cleanup exceeds the 5-second wait), the queued-follow-up path re-sends the response that is already visible on the chat platform. Add a fallback check: if the stream consumer's already_sent flag is True, treat delivery as confirmed even when final_response_sent is False. This only applies to the queued-follow-up path — the normal send path is unaffected. Regression tests in test_duplicate_reply_suppression.py. Fixes NousResearch#55806
|
Thanks for tracing the queued-follow-up race and providing the Discord logs from #55806. Problems
Suggested changes
Automated hermes-sweeper review. |
|
Thanks @liuhao1024 — you were the first to pin the missing confirmation on this path (2026-06-30). The guard landed in a stricter shape than a bare |
|
Thanks for tracing the queued-follow-up race and for the Discord logs on #55806 — the flag combination you identified ( Why not merged
Tracing every Reproduced against A real-import probe driving Also
If you can still reproduce a duplicate on current |
What does this PR do?
Fixes a race condition where the gateway's queued-follow-up path re-sends a response that was already delivered to the user by the stream consumer.
When a response includes image attachments, the stream consumer sends the text, then
_deliver_media_from_response()sends the images separately. If background processes finish around the same time and inject notifications, the queued-follow-up path checks_stream_confirmed_final_delivery()which only looks atfinal_response_sent. If the stream task was cancelled after the send (e.g. image delivery or cleanup exceeded the 5-second wait),final_response_sentstaysFalseand the response is re-sent as a duplicate.The fix adds a fallback check: if the stream consumer's
already_sentproperty isTrue, treat delivery as confirmed. This only applies to the queued-follow-up path — the normal send path is unaffected.Related Issue
Fixes #55806
Type of Change
Changes Made
gateway/run.py: Added fallback check in the queued-follow-up path — when_stream_confirmed_final_delivery()returns False but the stream consumer'salready_sentis True, skip the re-send. This prevents duplicate responses when the stream task is cancelled after delivering the message.tests/gateway/test_duplicate_reply_suppression.py: Added 2 regression tests covering the new fallback (consumer already sent → skip, consumer never sent → re-send).How to Test
pytest tests/gateway/test_duplicate_reply_suppression.py -q— all 28 tests should pass (26 existing + 2 new).Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/AScreenshots / Logs
From the issue reporter's gateway logs:
With the fix, the queued follow-up path sees
already_sent=Trueon the stream consumer and skips the re-send.