Skip to content

fix(gateway): don't hijack brand-new Telegram DM topics into the previous topic#31088

Closed
dillweed wants to merge 1 commit into
NousResearch:mainfrom
dillweed:fix/telegram-new-topic-hijack
Closed

fix(gateway): don't hijack brand-new Telegram DM topics into the previous topic#31088
dillweed wants to merge 1 commit into
NousResearch:mainfrom
dillweed:fix/telegram-new-topic-hijack

Conversation

@dillweed

Copy link
Copy Markdown
Contributor

Summary

Fixes #31086.

_recover_telegram_topic_thread_id in gateway/run.py rewrote the inbound thread_id of every brand-new Telegram DM topic to the user's most-recently-bound topic, hijacking the new conversation into the previous lane. The hijack was self-reinforcing — because the rewrite happens before _record_telegram_topic_binding, the new topic's binding row was never written, so the next inbound also looked "unknown" and was hijacked again. A freshly-created topic could never recover on its own.

The function was introduced in ede47a54b ("fix(gateway): pin Telegram DM-topic routing to user's current topic") to handle two real Telegram quirks:

  1. Lobby/stripped thread_id_build_message_event strips the thread_id on plain replies (needed for non-topic users, see [Bug]: Telegram DM sends fail with 'Message thread not found' — spurious thread_id from reply chains #3206).
  2. Cross-topic Reply leaks — Telegram delivers the other topic's message_thread_id when the user long-press-replies onto a message in a different topic.

The fix for (1) is correct and worth preserving. The fix for (2) (the "unknown topic → snap to most-recent" arm) cannot tell a Reply-leak apart from a brand-new topic — both look like an explicit, non-lobby thread_id that isn't yet in telegram_dm_topic_bindings. Since Reply leaks are rare and self-correct on the next message in the right topic, while new topics are common and trap permanently, the trade-off clearly favors trusting any explicit non-lobby thread_id.

Change

# before
inbound = str(source.thread_id or "")
is_lobby = not inbound or inbound in self._TELEGRAM_GENERAL_TOPIC_IDS
known = {str(b.get("thread_id") or "") for b in bindings}
if not is_lobby and inbound in known:
    return None
# ...falls through and rewrites unknown ids too

# after
inbound = str(source.thread_id or "")
is_lobby = not inbound or inbound in self._TELEGRAM_GENERAL_TOPIC_IDS
if not is_lobby:
    return None
# only lobby/missing ids fall through to recovery

Test

The previously-passing test test_recover_rewrites_unknown_thread_id_to_most_recent encoded the buggy behaviour. It's been renamed and inverted to test_recover_leaves_unknown_explicit_thread_id_alone, which captures the regression directly.

$ python -m pytest tests/gateway/test_telegram_topic_mode.py -q
42 passed in 1.07s

Verification on a live install

I patched a live gateway with this change, repaired the SQLite binding row for one previously-trapped topic, and ran two manual tests:

  • Sending a message in the previously-trapped topic — no telegram topic recovery log line; reply landed in the correct topic.
  • Sending a message that creates a brand-new topic from "All Messages" — no recovery line; reply landed in the new topic; SQLite binding row written for the new thread_id.

Before the fix, the gateway log showed every message in two separate fresh topics being redirected to a previously-active topic for hours:

07:27:20  telegram topic recovery: chat=... '573' -> 563
07:50:45  telegram topic recovery: chat=... '573' -> 563
... (hours of this) ...
10:45:17  telegram topic recovery: chat=... '652' -> 563    # different new topic, same hijack

After the fix:

11:35:56  inbound message: ... msg='Testing. Acknowledge.'                  # no recovery line
11:37:29  inbound message: ... msg='This is a new test topic. Acknowledge.' # no recovery line

Related

Sibling bug in the same subsystem: #20470 (post-split binding refresh). This PR does not address that; they are independent.

…ious topic

_recover_telegram_topic_thread_id rewrote the inbound thread_id of every
brand-new topic to the user's most-recently-bound topic, hijacking the
new conversation into the previous lane. The hijack is self-reinforcing:
because the rewrite happens before _record_telegram_topic_binding, the
new topic's binding row is never written, so the next inbound also looks
'unknown' and is hijacked again. A freshly-created topic never recovers
on its own.

The original commit (ede47a5) intended the 'unknown topic -> snap to
most-recent' arm to catch cross-topic Reply leaks, but those are far
rarer than legitimate new topics and self-correct on the next message
in the right topic. Drop that arm; trust any explicit, non-lobby
thread_id as-is. Lobby/missing-thread recovery (the stripped-plain-reply
case from NousResearch#3206 / non-topic-mode users) is preserved.

Updates the previously-passing test that encoded the buggy behaviour:
test_recover_rewrites_unknown_thread_id_to_most_recent is renamed and
inverted to test_recover_leaves_unknown_explicit_thread_id_alone.

Fixes NousResearch#31086
@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/gateway Gateway runner, session dispatch, delivery platform/telegram Telegram bot adapter duplicate This issue or pull request already exists labels May 23, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Duplicate of #28605 — same approach (narrow _recover_telegram_topic_thread_id recovery to lobby-only inputs). See also #29287 and #29546 (same fix, already marked as duplicates of #28605) and #30422 (competing approach: opt-in config flag).

@teknium1

Copy link
Copy Markdown
Contributor

Superseded by #31444 (#31444). You filed the canonical issue (#31086) with the root-cause analysis, the proposed patch, the local-repair SQL, and live verification — all of which became the basis for the salvage PR. You're credited as a co-author on the regression test commit. Thanks for the exemplary bug report. Closes #31086.

@teknium1 teknium1 closed this May 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery duplicate This issue or pull request already exists P1 High — major feature broken, no workaround platform/telegram Telegram bot adapter type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] _recover_telegram_topic_thread_id hijacks every brand-new Telegram DM topic into the previous topic

3 participants