Skip to content

fix(telegram): restore thread_id=1 handling for DMs (regression from 19b8416a8)#10942

Merged
Takhoffman merged 4 commits intoopenclaw:mainfrom
garnetlyx:fix/telegram-dm-thread-regression
Feb 15, 2026
Merged

fix(telegram): restore thread_id=1 handling for DMs (regression from 19b8416a8)#10942
Takhoffman merged 4 commits intoopenclaw:mainfrom
garnetlyx:fix/telegram-dm-thread-regression

Conversation

@garnetlyx
Copy link
Contributor

@garnetlyx garnetlyx commented Feb 7, 2026

Summary

Fixes regression introduced in commit 19b8416 that broke PR #848's fix for Telegram General topic (thread_id=1) handling.

Problem

Sub-agent announce messages fail in Telegram DMs with error: "Bad Request: message thread not found"

Root Cause

Commit 19b8416 ("fix: unify telegram thread handling") modified the logic to only skip thread_id=1 when scope === "forum":

// BEFORE (PR #848 - correct)
if (normalized === TELEGRAM_GENERAL_TOPIC_ID) {
  return undefined;  // Skips thread_id=1 for ALL cases
}

// AFTER (commit 19b8416a8 - BROKEN)
if (normalized === TELEGRAM_GENERAL_TOPIC_ID && thread.scope === "forum") {
  return undefined;  // Only skips when scope="forum"
}

In DMs with thread_id=1:

  • thread.scope = "dm"
  • Condition: 1 === 1 && "dm" === "forum"false
  • Result: Sends message_thread_id: 1 to Telegram API
  • Telegram response: "Bad Request: message thread not found"

Fix

Implements Option C (recommended approach) with two explicit checks:

// Never send thread_id for DMs (threads don't exist in private chats)
if (thread.scope === "dm") {
  return undefined;
}

// Telegram rejects message_thread_id=1 for General forum topic
if (normalized === TELEGRAM_GENERAL_TOPIC_ID) {
  return undefined;
}

Changes

  1. src/telegram/bot/helpers.ts: Updated buildTelegramThreadParams() with explicit DM check
  2. src/telegram/bot/helpers.test.ts: Updated tests to reflect new behavior
  3. Added JSDoc documentation explaining thread ID behavior per chat type
  4. Added edge case tests for DM thread normalization

Testing

  • ✅ All tests passing (25/25)
  • ✅ DM thread IDs are now correctly skipped
  • ✅ Forum General topic (id=1) handling restored
  • ✅ Regular group behavior unchanged
  • ✅ Typing indicators unaffected (separate function)

Related

Greptile Overview

Greptile Summary

  • Fixes Telegram DM sends by omitting message_thread_id entirely when thread.scope === "dm".
  • Restores special-casing for forum “General” topic (thread_id=1) by omitting it from API params.
  • Updates buildTelegramThreadParams docs and adjusts/extends unit tests to cover DM and edge-value normalization.
  • Adds dependency override for [email protected] and updates lockfile accordingly; .gitignore updated to ignore docs/SESSION_LOG.md.

Confidence Score: 4/5

  • This PR is likely safe to merge, with one small logic edge case to confirm/fix around falsy thread IDs.
  • The Telegram thread param change is narrowly scoped and tests cover DM omission and General-topic handling. The main remaining concern is the early !thread?.id guard, which unconditionally drops id=0 before normalization, potentially conflicting with intended semantics if upstream can produce 0 as a meaningful value.
  • src/telegram/bot/helpers.ts

@openclaw-barnacle openclaw-barnacle bot added the channel: telegram Channel integration: telegram label Feb 7, 2026
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines 74 to +76
}
const normalized = Math.trunc(thread.id);
if (normalized === TELEGRAM_GENERAL_TOPIC_ID && thread.scope === "forum") {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Falsy thread IDs dropped

buildTelegramThreadParams() returns early on if (!thread?.id) which treats id=0 as “no thread”. But the function then normalizes with Math.trunc(thread.id) and the tests explicitly exercise id: 0 as an “edge value”, so callers can pass numeric values outside the forum-topic range. If 0 is a valid sentinel/thread id in any upstream input, it will be silently omitted regardless of scope, which is inconsistent with the later normalization logic. Consider changing the guard to only skip on thread?.id == null (or typeof thread.id !== "number") so 0 is handled intentionally.

Also appears in: src/telegram/bot/helpers.test.ts (test asserts id: 0 behavior).

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/telegram/bot/helpers.ts
Line: 74:76

Comment:
**Falsy thread IDs dropped**

`buildTelegramThreadParams()` returns early on `if (!thread?.id)` which treats `id=0` as “no thread”. But the function then normalizes with `Math.trunc(thread.id)` and the tests explicitly exercise `id: 0` as an “edge value”, so callers can pass numeric values outside the forum-topic range. If `0` is a valid sentinel/thread id in any upstream input, it will be silently omitted regardless of `scope`, which is inconsistent with the later normalization logic. Consider changing the guard to only skip on `thread?.id == null` (or `typeof thread.id !== "number"`) so `0` is handled intentionally.

Also appears in: `src/telegram/bot/helpers.test.ts` (test asserts `id: 0` behavior).

How can I resolve this? If you propose a fix, please make it concise.

@garnetlyx
Copy link
Contributor Author

✅ Fixed: Addressed the falsy thread IDs issue

Thanks for the thorough review! I've implemented the suggested fix:

Changes made:

  1. Fixed guard condition: Changed from if (!thread?.id) to if (thread?.id == null) in buildTelegramThreadParams()
  2. Added test coverage: Added explicit test for id: 0 behavior in non-DM scopes
  3. Preserved DM behavior: id: 0 is still correctly skipped for DMs (threads don't exist in private chats)

Result:

  • id: 0 is now handled intentionally (not silently dropped)
  • Consistent behavior between early guard and later normalization
  • All tests passing (26/26)

The fix ensures that thread ID 0 is treated as a valid numeric value while maintaining the correct semantics for DMs vs forums/regular groups.

@Takhoffman Takhoffman self-assigned this Feb 7, 2026
@rizwanreza
Copy link

Confirmed that this fixes the issue with Telegram receiving announce messages.

garnetlyx pushed a commit to garnetlyx/openclaw that referenced this pull request Feb 8, 2026
- Change test from expecting message_thread_id:1 for DMs to expecting no thread_id
- DMs (private chats) don't support threads, so thread_id should be omitted
- This aligns with the fix in buildTelegramThreadParams that returns undefined for dm scope
- Fixes CI failure in PR openclaw#10942
@garnetlyx garnetlyx force-pushed the fix/telegram-dm-thread-regression branch from 6a00bb8 to 8f7e4d8 Compare February 8, 2026 13:52
garnetliu and others added 4 commits February 15, 2026 10:10
- DMs should not have thread_id (private chats don't support threads)
- Restore PR openclaw#848 fix: skip thread_id=1 for General forum topics
- Fixes regression from commit 19b8416 that broke sub-agent announce in DMs
- Closes openclaw#10926
- Change guard condition from if (!thread?.id) to if (thread?.id == null)
- Allows thread id 0 to pass through and be handled appropriately
- Fixes Greptile bot review comment about falsy thread IDs being dropped
- Change test from expecting message_thread_id:1 for DMs to expecting no thread_id
- DMs (private chats) don't support threads, so thread_id should be omitted
- This aligns with the fix in buildTelegramThreadParams that returns undefined for dm scope
- Fixes CI failure in PR openclaw#10942
@Takhoffman Takhoffman force-pushed the fix/telegram-dm-thread-regression branch from 8f7e4d8 to a49d607 Compare February 15, 2026 16:21
@Takhoffman Takhoffman merged commit cc0bfa0 into openclaw:main Feb 15, 2026
8 of 9 checks passed
@Takhoffman
Copy link
Contributor

PR #10942 - fix(telegram): restore thread_id=1 handling for DMs (regression from 19b8416) (#10942)

Merged via squash.

  • Merge commit: cc0bfa0
  • Verified:
    • pnpm install --frozen-lockfile
    • pnpm build
    • pnpm test:macmini
    • pnpm check (fails on unrelated pre-existing tsgo issue in src/slack/monitor/slash.test-harness.ts)
  • Changes made:
    • M CHANGELOG.md
    • M src/telegram/bot/delivery.test.ts
    • M src/telegram/bot/helpers.test.ts
    • M src/telegram/bot/helpers.ts
    • M src/telegram/draft-stream.test.ts
  • Why these changes were made:
    • Fix Telegram DM reply/draft failures caused by sending message_thread_id in private chats, keep forum topic behavior (omit General topic id=1), and align regression tests/changelog with the corrected contract.
  • Changelog: CHANGELOG.md updated=true required=true opt_out=false

Thanks @garnetlyx!

@Lukavyi
Copy link
Contributor

Lukavyi commented Feb 16, 2026

@garnetlyx @Takhoffman This fix introduces a regression for Telegram DMs with Topics enabled.

Telegram private chats (1:1 with a bot) do support topics — when the user enables them, message_thread_id is used to route replies to the correct thread. The blanket scope === "dm" → return undefined in buildTelegramThreadParams drops thread_id for all DM messages, causing replies to land in the General chat instead of the correct topic thread.

The fix for General topic (thread_id=1) is correct, but it shouldn't discard all thread IDs for DM scope — only thread_id=1 (same as forum scope).

There are several open PRs trying to address this: #6192, #17433, #12864, #15818, #7261, #16882, #2778 and issue #12625.

@Lukavyi
Copy link
Contributor

Lukavyi commented Feb 16, 2026

For reference: Telegram Bot API 9.3 (December 31, 2025) officially added Topics in private chatsmessage_thread_id is fully supported in DMs when the bot has topics enabled.

See: https://core.telegram.org/bots/api-changelog#december-31-2025

The API added has_topics_enabled to the User class specifically to detect this. The fix should check this field instead of blanket-dropping thread_id for all DMs.

@Leonccaa
Copy link

Regression note: we are seeing a follow-up behavior regression after the DM thread-id omission fix in 2026.2.15.\n\nTracked in: #17960\n\nSymptom: in Telegram private topics, inbound routing remains thread-aware, but outbound replies often appear in the main tab (topic UX regression).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: telegram Channel integration: telegram size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(telegram): restore thread_id=1 handling for DMs (regression from 19b8416a8)

5 participants

Comments