From 4eb8479ebdce46712d00694a82faf2aa1675497b Mon Sep 17 00:00:00 2001 From: HuangYuChuh Date: Wed, 29 Apr 2026 17:55:04 +0800 Subject: [PATCH] fix(gateway): delete partial message after fallback send on flood control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When Telegram flood control triggers 3+ consecutive edit failures, the stream consumer enters fallback mode and sends the complete response as a new message. This leaves the user seeing two messages: a frozen partial (with cursor) and the full duplicate. After the fallback chunks are sent successfully, delete the original partial message so the user only sees one complete response. The delete is best-effort — if it fails (e.g. flood still active, missing permissions), the full answer is still delivered. Fixes #16668 --- gateway/stream_consumer.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/gateway/stream_consumer.py b/gateway/stream_consumer.py index cfd5e9f8d8a..1c83e0233db 100644 --- a/gateway/stream_consumer.py +++ b/gateway/stream_consumer.py @@ -640,6 +640,7 @@ class GatewayStreamConsumer: safe_limit = max(500, raw_limit - 100) chunks = self._split_text_chunks(continuation, safe_limit) + stale_message_id = self._message_id # partial message to clean up last_message_id: Optional[str] = None last_successful_chunk = "" sent_any_chunk = False @@ -687,6 +688,16 @@ class GatewayStreamConsumer: # so any stale tool-progress bubble gets closed off. self._notify_new_message() + # Remove the frozen partial message so the user only sees the + # complete fallback response. Best-effort — if the delete fails + # (e.g. flood control still active, or bot lacks permission), the + # partial message remains but at least the full answer was delivered. + if stale_message_id and stale_message_id != last_message_id: + try: + await self.adapter.delete_message(self.chat_id, stale_message_id) + except Exception: + pass + self._message_id = last_message_id self._already_sent = True self._final_response_sent = True