Skip to content

fix(telegram): streamed overflow finals lost under flood control are silently marked delivered - #70740

Closed
jacques-commits wants to merge 1 commit into
NousResearch:mainfrom
jacques-commits:fix/t-697b9c65-telegram-overflow-delivery
Closed

fix(telegram): streamed overflow finals lost under flood control are silently marked delivered#70740
jacques-commits wants to merge 1 commit into
NousResearch:mainfrom
jacques-commits:fix/t-697b9c65-telegram-overflow-delivery

Conversation

@jacques-commits

Copy link
Copy Markdown

Summary

When a streamed Telegram final response overflows the 4096-char limit and the final split then hits flood control (429), the gateway falsely concludes the message was fully delivered. The user sees only the truncated (1/2) preview; the (2/2) chunk is silently lost, no retry fires, and the delivery-obligation ledger (#67181) never engages.

Root cause

  1. On a non-final oversized edit, the Telegram adapter truncates the streaming preview to truncate_message(...)[0] — only the visible first chunk — but returns success=True without reporting the truncated text.
  2. GatewayStreamConsumer therefore records the full oversized input in _last_sent_text, even though Telegram only displayed chunk 1.
  3. When the final overflow split fails under flood control, the cursor-cleanup heuristic sees _visible_prefix() == final_text, falsely sets _final_content_delivered=True, and suppresses both fallback delivery and the ledger obligation.

Observed in production (0.19.0, 3ef6bbd20): a 4,681-char response delivered (1/2) only; gateway.run logged content_delivered=True while delivery_obligations held no row for the session, so the loss was silent and unrecoverable.

Fix

The Telegram adapter now reports raw_response["delivered_text"] for truncated/deduplicated streaming previews, and the stream consumer records what actually landed rather than what was submitted. A flood-controlled final split therefore enters full fallback delivery instead of declaring a clipped preview complete.

Changed: gateway/platforms/base.py, gateway/stream_consumer.py, plugins/platforms/telegram/adapter.py, plus focused regressions in tests/gateway/test_stream_consumer_fresh_final.py and tests/gateway/test_telegram_format.py.

Verification

  • Unpatched 0.19.0 manual regression reproduces the bug (clipped preview falsely marked fully delivered).
  • Patched build: clipped preview tracked, fallback delivered the complete response, stale-partial cleanup exercised.
  • Live Telegram test: a 5,569-char streamed response delivered completely as two messages with final_response_sent=True and final_content_delivered=True.
  • Focused pytest regressions added for both the consumer bookkeeping and the adapter's delivered-text reporting.

🤖 Generated with Claude Code

@alt-glitch alt-glitch added type/bug Something isn't working comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins platform/telegram Telegram bot adapter area/streaming Streaming responses: gateway delivery, provider wire P2 Medium — degraded but workaround exists sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Jul 24, 2026
@Sinha-Aditya

Copy link
Copy Markdown

Sibling production reproduction: final send suppressed after max-iteration fallback

We observed a related Telegram delivery failure after a long agent run exhausted its iteration budget:

  • The run used 120/120 API/tool-calling iterations.
  • The max-iteration handler generated a 1,132-character fallback response.
  • Gateway log recorded:
response ready: ... api_calls=120 response=1132 chars
Suppressing normal final send ... final delivery already confirmed (streamed=True previewed=False content_delivered=True)

The user reported that the bot stopped without a usable final response. No Telegram failed to send error was logged for this incident. The gateway suppressed the normal final send because the streamed-delivery state claimed the content had already been delivered.

The final response was below Telegram's normal 4,096-character message limit, so this appears to be a sibling false-positive delivery-state path, not only an overflow-final case. It may be worth ensuring that the final-delivery suppression decision verifies that the complete finalized response—not merely a streamed preview/progress message—was actually delivered.

Operational workaround used locally: streaming.enabled: false until the delivery-state fix is available.

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the careful investigation and focused regressions.

Automated hermes-sweeper review found this delivery guarantee is already implemented on current main:

  • 04898631cb72 (fix(telegram): recover final delivery after stream flood) added Telegram's empty-tail recovery path.
  • gateway/stream_consumer.py:1257-1286 sends a fresh full final when a failed final edit leaves no continuation, and clears delivery flags after a confirmed failed recovery send.
  • gateway/stream_consumer.py:1429-1499 sends the complete final_text and marks it delivered only after successful transport confirmation.
  • plugins/platforms/telegram/adapter.py:629-633 enables that recovery for Telegram.

The PR's outcome—preventing a clipped preview from being silently treated as completed delivery—is therefore already provided on main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/streaming Streaming responses: gateway delivery, provider wire comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins P2 Medium — degraded but workaround exists platform/telegram Telegram bot adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:implemented-on-main Sweeper: behavior already present on current main sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants