From aef297a45eab2afabd0084e62a5e7666eee68981 Mon Sep 17 00:00:00 2001 From: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com> Date: Sat, 9 May 2026 14:04:17 +0530 Subject: [PATCH] fix(telegram): skip send_chat_action for DM topic reply-fallback lanes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The send path uses Hermes' reply-anchor fallback for DM topic lanes (message_thread_id + reply_to_message_id), but send_chat_action only accepts message_thread_id — Telegram's Bot API 10.0 rejects it for these lanes. Without this short-circuit, every typing tick (~every 2s during agent runs) makes a doomed API call that gets logged as a 'thread not found' debug warning. Skip the call entirely when the metadata indicates a DM topic reply-fallback lane; the user-visible behavior is unchanged (no typing indicator either way for these lanes), but the logs stay clean. Identified during salvage review of #22053. --- gateway/platforms/telegram.py | 8 +++++ .../gateway/test_telegram_thread_fallback.py | 30 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/gateway/platforms/telegram.py b/gateway/platforms/telegram.py index f692aa4fd16..2949680b50f 100644 --- a/gateway/platforms/telegram.py +++ b/gateway/platforms/telegram.py @@ -2860,6 +2860,14 @@ class TelegramAdapter(BasePlatformAdapter): if self._bot: try: _typing_thread = self._metadata_thread_id(metadata) + # Skip the Bot API call entirely for Hermes-created DM topic + # lanes: send_chat_action only accepts message_thread_id, which + # Telegram's Bot API 10.0 rejects for these lanes. The send + # path uses the reply-anchor fallback instead, but typing has + # no equivalent — skipping avoids noisy "thread not found" + # debug logs on every typing tick. + if metadata and metadata.get("telegram_dm_topic_reply_fallback"): + return message_thread_id = self._message_thread_id_for_typing(_typing_thread) # No retry-without-thread fallback here: _message_thread_id_for_typing # already maps the forum General topic to None, so any non-None value diff --git a/tests/gateway/test_telegram_thread_fallback.py b/tests/gateway/test_telegram_thread_fallback.py index 92702f6fd00..e31753cc2b7 100644 --- a/tests/gateway/test_telegram_thread_fallback.py +++ b/tests/gateway/test_telegram_thread_fallback.py @@ -235,6 +235,36 @@ async def test_send_typing_does_not_fall_back_to_root_for_dm_topic(): ] +@pytest.mark.asyncio +async def test_send_typing_skips_api_call_for_dm_topic_reply_fallback(): + """Hermes-created DM topic lanes have no working Bot API typing route. + + ``send_chat_action`` only accepts ``message_thread_id``, which Telegram's + Bot API 10.0 rejects for these lanes — the call would silently fail and + log a "thread not found" warning every typing tick (every 2s). Skipping + the call entirely keeps logs clean while preserving the user-visible + behavior (no typing indicator either way for these lanes). + """ + adapter = _make_adapter() + call_log = [] + + async def mock_send_chat_action(**kwargs): + call_log.append(dict(kwargs)) + + adapter._bot = SimpleNamespace(send_chat_action=mock_send_chat_action) + + await adapter.send_typing( + "12345", + metadata={ + "thread_id": "20197", + "telegram_dm_topic_reply_fallback": True, + "telegram_reply_to_message_id": "462", + }, + ) + + assert call_log == [] + + @pytest.mark.asyncio async def test_send_retries_without_thread_on_thread_not_found(): """When message_thread_id causes 'thread not found', retry without it."""