From post-merge audit of PR #28488 (#27937 salvage, route resumed DM topic sends via direct_messages_topic_id).
Bug
_should_retry_without_dm_topic_reply_anchor in gateway/platforms/telegram.py:653 requires reply_to_message_id is not None to fire. That guard is correct for the original anchor-stale case it was built for. But after #27937 added the direct_messages_topic_id fallback path for resumed/synthetic sends without an anchor, there's no retry coverage for the case where Bot API rejects the topic id itself.
Scenario
- Gateway resumes after a shutdown.
- Synthetic event has no
message_id, no reply anchor.
_thread_kwargs_for_send returns {message_thread_id: None, direct_messages_topic_id: <hermes_thread_id>}.
- Bot API rejects (e.g., topic deleted from Telegram side, or stale binding).
_should_retry_without_dm_topic_reply_anchor returns False (no reply_to_message_id) → exception propagates → message lost.
Today this is unlikely because Hermes-created DM topics use the same id Telegram uses for message_thread_id, so they shouldn't reject. But:
- Stale bindings (topic deleted on Telegram side after Hermes cached the id) → BadRequest.
- Telegram could change the contract; we'd be unprotected.
Proposed fix
Broaden the predicate to ALSO fire when:
metadata.get('direct_messages_topic_id') is set, AND
- Error is BadRequest, AND
- Error message indicates a topic/thread routing issue (e.g., "message thread not found", "DIRECT_MESSAGES_TOPIC_INVALID", or just any BadRequest involving "topic" / "thread").
Retry path already correctly strips both message_thread_id and direct_messages_topic_id (gateway/platforms/telegram.py:697), so widening the predicate alone is the fix.
Severity
Low likelihood today, moderate impact (synthetic notification lost). Defensive hardening.
From post-merge audit of PR #28488 (#27937 salvage,
route resumed DM topic sends via direct_messages_topic_id).Bug
_should_retry_without_dm_topic_reply_anchoringateway/platforms/telegram.py:653requiresreply_to_message_id is not Noneto fire. That guard is correct for the original anchor-stale case it was built for. But after #27937 added thedirect_messages_topic_idfallback path for resumed/synthetic sends without an anchor, there's no retry coverage for the case where Bot API rejects the topic id itself.Scenario
message_id, no reply anchor._thread_kwargs_for_sendreturns{message_thread_id: None, direct_messages_topic_id: <hermes_thread_id>}._should_retry_without_dm_topic_reply_anchorreturns False (noreply_to_message_id) → exception propagates → message lost.Today this is unlikely because Hermes-created DM topics use the same id Telegram uses for
message_thread_id, so they shouldn't reject. But:Proposed fix
Broaden the predicate to ALSO fire when:
metadata.get('direct_messages_topic_id')is set, ANDRetry path already correctly strips both
message_thread_idanddirect_messages_topic_id(gateway/platforms/telegram.py:697), so widening the predicate alone is the fix.Severity
Low likelihood today, moderate impact (synthetic notification lost). Defensive hardening.