From e5e96e8bb57aaf4c0a8205ef5c1885f4519301a4 Mon Sep 17 00:00:00 2001 From: kshitij <82637225+kshitijk4poor@users.noreply.github.com> Date: Fri, 7 Aug 2026 18:28:18 +0530 Subject: [PATCH] fix: harden _await_disconnect_step against outer cancellation + add claim keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to #80700: 1. _await_disconnect_step was missing the try/except CancelledError around asyncio.wait() that _await_adapter_cleanup_with_timeout already has. When the outer fatal-handler timeout cancels disconnect() mid-step, asyncio.wait does NOT cancel its inner task — the task was orphaned with no observer. Add the same cancel+detach+re-raise pattern. 2. _queue_retryable_fatal_platform omitted credential_claim/listener_claim keys that all 3 startup-path queue sites include. These are consumed by the multiplex reservation logic to prevent secondary profiles from taking the endpoint while a primary is queued. Pre-existing latent bug — now fixed since the extraction makes it trivial. --- gateway/run.py | 6 ++++++ plugins/platforms/telegram/adapter.py | 17 +++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/gateway/run.py b/gateway/run.py index ddb7de0e18516..125185aeaa816 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -7270,6 +7270,12 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew "config": platform_config, "attempts": 0, "next_retry": time.monotonic(), + "credential_claim": self._adapter_credential_claim( + adapter.platform, adapter + ), + "listener_claim": self._adapter_listener_claim( + adapter.platform, adapter + ), } logger.info( "%s queued for background reconnection", diff --git a/plugins/platforms/telegram/adapter.py b/plugins/platforms/telegram/adapter.py index 59da0a5a089bc..eea8e316ff796 100644 --- a/plugins/platforms/telegram/adapter.py +++ b/plugins/platforms/telegram/adapter.py @@ -4318,10 +4318,19 @@ class TelegramAdapter(BasePlatformAdapter): via ``_consume_abandoned_task``. """ task = asyncio.ensure_future(awaitable) - if timeout <= 0: - done, _pending = await asyncio.wait({task}) - else: - done, _pending = await asyncio.wait({task}, timeout=timeout) + try: + if timeout <= 0: + done, _pending = await asyncio.wait({task}) + else: + done, _pending = await asyncio.wait({task}, timeout=timeout) + except asyncio.CancelledError: + # Outer cancellation (e.g. the fatal handler's outer timeout) must + # not orphan the inner task — asyncio.wait does NOT cancel its + # futures when itself cancelled (#80598). Mirror the pattern used + # by GatewayRunner._await_adapter_cleanup_with_timeout. + task.cancel() + task.add_done_callback(_consume_abandoned_task) + raise if task in done: # Intentional cancels (heartbeat / identity / lifecycle) surface as # CancelledError — swallow so disconnect keeps advancing.