Telegram gateway reconnect and shutdown reliability fixes
Cluster 35205 · gateway. Data as of
Nothing open on GitHub. Fix merged in #54076 on 28 Jun 2026. Check it covers your case.
The short answer
- Known?
- Yes. 4 related items grouped here, 1 issue already closed.
- Fix event
- A fix was merged to main in PR #54076. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
resolved, deciding PR #54076. That is separate from the fix event. - Still open
- Nothing open on GitHub.
These issues and pull requests address bugs in the Telegram gateway around send_path_degraded not clearing after reconnect, network error recovery delegation, polling readiness timing, and missing user feedback during shutdown.
Open issues0
Open PRs0
Fixes on record
-
#54076 fix(telegram): clear send_path_degraded on successful reconnect (#35205)
files:
infographic/telegram-send-path-35205/infographic.png,plugins/platforms/telegram/adapter.py,tests/gateway/test_telegram_send_path_health.pyShow the diff
-
plugins/platforms/telegram/adapter.py@@ -1572,6 +1572,17 @@ async def _handle_polling_network_error(self, error: Exception) -> None: self.name, attempt, ) self._polling_network_error_count = 0 + # start_polling() succeeding IS the recovery signal: the long-poll + # connection is live again, so clear the degraded flag immediately + # rather than blocking all outbound sends for the full + # HEARTBEAT_PROBE_DELAY window. The deferred probe below is a + # defensive re-check — if it later detects a silent wedge (PTB + # running=True but consumer task dead) it re-enters the ladder, + # which re-sets _send_path_degraded. Without this clear here, a + # clean reconnect leaves the flag stuck True until the 60s probe + # (or forever, if the probe is never scheduled), blocking the send + # path even though the bot has fully recovered. See #35205. + self._send_path_degraded = False # start_polling() returning is necessary but not sufficient: # PTB's Updater can be left in a state where `running` is True # but the underlying long-poll task is wedged on a stale httpx -
tests/gateway/test_telegram_send_path_health.py@@ -66,24 +66,71 @@ async def test_send_short_circuits_when_path_degraded(): @pytest.mark.asyncio async def test_reconnect_storm_sets_and_heartbeat_clears_flag(monkeypatch): - """_handle_polling_network_error sets the flag; a successful heartbeat - probe in _verify_polling_after_reconnect clears it.""" + """_handle_polling_network_error sets the flag while reconnecting; if the + reconnect attempt itself raises (polling not yet healthy), the flag stays + True until a later successful heartbeat probe in + _verify_polling_after_reconnect clears it.""" adapter = _make_adapter() adapter._app = MagicMock() adapter._app.updater = MagicMock() adapter._app.updater.running = True adapter._app.updater.stop = AsyncMock() - adapter._app.updater.start_polling = AsyncMock() + # First start_polling attempt fails — the reconnect handler must leave the + # flag set (path still unhealthy) and not clear it prematurely. + adapter._app.updater.start_polling = AsyncMock(side_effect=OSError("still down")) adapter._app.bot = MagicMock() adapter._app.bot.get_me = AsyncMock(return_value=MagicMock()) adapter._polling_error_callback_ref = AsyncMock() monkeypatch.setattr( "plugins.platforms.telegram.adapter.Update", MagicMock(ALL_TYPES=[]) ) + # Suppress the self-rescheduled retry so the test doesn't recurse. + monkeypatch.setattr( + "plugins.platforms.telegram.adapter.asyncio.ensure_future", MagicMock() + ) - await adapter._handle_polling_network_error(OSError("Bad Gateway")) + with patch("plugins.platforms.telegram.adapter.asyncio.sleep", new_callable=AsyncMock): + await adapter._handle_polling_network_error(OSError("Bad Gateway")) + # start_polling failed → path still degraded. assert adapter._send_path_degraded is True + # Now the deferred probe runs against a recovered (running) updater and + # a responsive bot — it clears the flag. + adapter._app.updater.running = True with patch("plugins.platforms.telegram.adapter.asyncio.sleep", new_callable=AsyncMock): await adapter._verify_polling_after_reconnect() assert adapter._send_path_degraded is False + + [redacted:email] +async def test_successful_reconnect_clears_flag_without_probe(monkeypatch): + """Regression for #35205: a successful start_polling() clears + _send_path_degraded immediately, so outbound sends are not blocked for + the full HEARTBEAT_PROBE_DELAY window (and never get stuck True if the + deferred probe is never scheduled / never runs).""" + adapter = _make_adapter() + adapter._app = MagicMock() + adapter._app.updater = MagicMock() + adapter._app.updater.running = True + adapter._app.updater.stop = AsyncMock() + adapter._app.updater.start_polling = AsyncMock() + adapter._app.bot = MagicMock() + adapter._app.bot.get_me = AsyncMock(return_value=MagicMock()) + adapter._polling_error_callback_ref = AsyncMock() + monkeypatch.setattr( + "plugins.platforms.telegram.adapter.Update", MagicMock(ALL_TYPES=[]) + ) + # Don't let the deferred probe run — prove the clear happens in the + # reconnect handler itself, not in _verify_polling_after_reconnect. + monkeypatch.setattr( + adapter, "_verify_polling_after_reconnect", AsyncMock() + ) + + with patch("plugins.platforms.telegram.adapter.asyncio.sleep", new_callable=AsyncMock): + await adapter._handle_polling_network_error(OSError("Bad Gateway")) + + assert adapter._send_path_degraded is False + assert adapter._polling_network_error_count == 0 + # And send() works again right away. + result = await adapter.send("123", "hello") + assert result.success is True
-
2 PRs closed without merging
From the close comments: done elsewhere 2
Closed issues · 1
- Closed#35205Telegram gateway: send_path_degraded stays true after successful reconnect, blocking all outbound messages
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.50: 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
- Subsystem
- gateway
- Merged PRs
- #54076
- Closed issues
- 1
- Attempts
- 2 PRs closed without merging
- Fix link checked
- 28 Jun 2026