Telegram streaming message left frozen with cursor character
Cluster 7183. Data as of
Nothing open on GitHub. Fix merged in #12472 on 19 Apr 2026. Check it covers your case.
The short answer
- Known?
- Yes. 3 related items grouped here, 1 issue already closed.
- Fix event
- A fix was merged to main in PR #12472. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
unresolved. That is separate from the fix event. - Still open
- Nothing open on GitHub.
These items concern a streaming message in the gateway (reported for Telegram) that can stay frozen with a cursor (▉) when the final cursor-removal edit fails after a tool call. Two members are fix proposals that strip the cursor when the fallback continuation is empty, and one is the bug report; their outcomes are not stated in the titles.
Open issues0
Open PRs0
Fixes on record
-
#12472 fix(gateway): strip cursor from frozen message on empty fallback continuation (#7183)
files:
gateway/stream_consumer.py,tests/gateway/test_stream_consumer.pyShow the diff
-
gateway/stream_consumer.py@@ -571,6 +571,30 @@ async def _send_fallback_final(self, text: str) -> None: if final_text.strip() and final_text != self._visible_prefix(): continuation = final_text else: + # Defence-in-depth for #7183: the last edit may still show the + # cursor character because fallback mode was entered after an + # edit failure left it stuck. Try one final edit to strip it + # so the message doesn't freeze with a visible ▉. Best-effort + # — if this edit also fails (flood control still active), + # _try_strip_cursor has already been called on fallback entry + # and the adaptive-backoff retries will have had their shot. + if ( + self._message_id + and self._last_sent_text + and self.cfg.cursor + and self._last_sent_text.endswith(self.cfg.cursor) + ): + clean_text = self._last_sent_text[:-len(self.cfg.cursor)] + try: + result = await self.adapter.edit_message( + chat_id=self.chat_id, + message_id=self._message_id, + content=clean_text, + ) + if result.success: + self._last_sent_text = clean_text + except Exception: + pass self._already_sent = True self._final_response_sent = True return -
tests/gateway/test_stream_consumer.py@@ -1216,3 +1216,87 @@ async def test_default_mode_still_triggers_intermediate_edits(self): # text, the consumer may send then edit, or just send once at got_done. # The key assertion: this doesn't break. assert adapter.send.call_count >= 1 + + +# ── Cursor stripping on fallback (#7183) ──────────────────────────────────── + + +class TestCursorStrippingOnFallback: + """Regression: cursor must be stripped when fallback continuation is empty (#7183). + + When _send_fallback_final is called with nothing new to deliver (the visible + partial already matches final_text), the last edit may still show the cursor + character because fallback mode was entered after a failed edit. Before the + fix this would leave the message permanently frozen with a visible ▉. + """ + + @pytest.mark.asyncio + async def test_cursor_stripped_when_continuation_empty(self): + """_send_fallback_final must attempt a final edit to strip the cursor.""" + adapter = MagicMock() + adapter.MAX_MESSAGE_LENGTH = 4096 + adapter.edit_message = AsyncMock( + return_value=SimpleNamespace(success=True, message_id="msg-1") + ) + + consumer = GatewayStreamConsumer( + adapter, "chat-1", + config=StreamConsumerConfig(cursor=" ▉"), + ) + consumer._message_id = "msg-1" + consumer._last_sent_text = "Hello world ▉" + consumer._fallback_final_send = False + + await consumer._send_fallback_final("Hello world") + + adapter.edit_message.assert_called_once() + call_args = adapter.edit_message.call_args + assert call_args.kwargs["content"] == "Hello world" + assert consumer._already_sent is True + # _last_sent_text should reflect the cleaned text after a successful strip + assert consumer._last_sent_text == "Hello world" + + @pytest.mark.asyncio + async def test_cursor_not_stripped_when_no_cursor_configured(self): + """No edit attempted when cursor is not configured.""" + adapter = MagicMock() + adapter.MAX_MESSAGE_LENGTH = 4096 + adapter.edit_message = AsyncMock() + + consumer = GatewayStreamConsumer( + adapter, "chat-1", + config=StreamConsumerConfig(cursor=""), + ) + consumer._message_id = "msg-1" + consumer._last_sent_text = "Hello world" + consumer._fallback_final_send = False + + await consumer._send_fallback_final("Hello world") + + adapter.edit_message.assert_not_called() + assert consumer._already_sent is True + + @pytest.mark.asyncio + async def test_cursor_strip_edit_failure_handled(self): + """If the cursor-stripping edit itself fails, it must not crash and + must not corrupt _last_sent_text.""" + adapter = MagicMock() + adapter.MAX_MESSAGE_LENGTH = 4096 + adapter.edit_message = AsyncMock( + return_value=SimpleNamespace(success=False, error="flood_control") + ) + + consumer = GatewayStreamConsumer( + adapter, "chat-1", + config=StreamConsumerConfig(cursor=" ▉"), + ) + consumer._message_id = "msg-1" + consumer._last_sent_text = "Hello ▉" + consumer._fallback_final_send = False + + await consumer._send_fallback_final("Hello") + + # Should still set already_sent despite the cursor-strip edit failure + assert consumer._already_sent is True + # _last_sent_text must NOT be updated when the edit failed + assert consumer._last_sent_text == "Hello ▉"
-
1 PR closed without merging
From the close comments: done elsewhere 1
-
#7429 fix(gateway): strip cursor from frozen message when fallback continuation is empty · done elsewhere →
#12472,#10807
Closed issues · 1
- Closed#7183[Bug]: Telegram streaming message frozen with cursor (▉) when final cursor-removal edit fails after tool call
How this cluster was grouped
-
Link evidence (
structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference. -
Pairwise link density
0.67: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the LabAt a glance
- Merged PRs
- #12472
- Closed issues
- 1
- Attempts
- 1 PR closed without merging
- Fix link checked
- 19 Apr 2026. This confirmation is old and may be out of date (a revert or reopen would not have invalidated it here); re-verify.