-
-
Notifications
You must be signed in to change notification settings - Fork 69.4k
[Bug]: Subagent announce delivers to wrong Telegram DM topic (stale threadId) #52217
Description
Bug Description
When a subagent is spawned from a Telegram DM topic conversation (using sessions_spawn), the completion announce is delivered to the main chat / wrong topic instead of the originating topic.
Steps to Reproduce
- Open a DM topic with the bot (e.g. topic ID
1045687) - Trigger a
sessions_spawnsubagent from that topic - Wait for subagent to complete
- The announce result appears in the main chat (General topic or a different topic), not in topic
1045687
Expected Behavior
The announce should be delivered to the same topic where the spawn was triggered, using the threadId captured in requesterOrigin at spawn time.
Actual Behavior
The announce is delivered to the main chat without message_thread_id, or to a stale topic from the session entry's lastThreadId.
Analysis
The requesterOrigin correctly captures threadId at spawn time in spawnSubagentDirect() (line ~333 in subagent-spawn.ts):
const requesterOrigin = normalizeDeliveryContext({
channel: ctx.agentChannel,
accountId: ctx.agentAccountId,
to: ctx.agentTo,
threadId: ctx.agentThreadId,
});However, at announce time in runSubagentAnnounceFlow(), the main session entry (agent:main:main) may have a stale lastThreadId / deliveryContext.threadId from a different topic interaction that happened between spawn and completion.
When expectsCompletionMessage=true (the default), the flow goes through resolveSubagentCompletionOrigin() -> createBoundDeliveryRouter().resolveDestination(). For non-thread-bound spawns, this falls back correctly to requesterOrigin. But the final delivery path through sendSubagentAnnounceDirectly() may lose the threadId.
Additionally, there is a guard in resolveAnnounceOrigin() (added in commit 8178ea472d for Discord threading) that actively strips threadId from the fallback entry when the requester has to but no threadId:
const entryForMerge =
normalizedRequester?.to &&
normalizedRequester.threadId == null &&
normalizedEntry?.threadId != null
? (() => {
const { threadId: _ignore, ...rest } = normalizedEntry;
return rest;
})()
: normalizedEntry;Observability Gap
The gateway log only shows [telegram] sendMessage ok chat=63448508 message=XXXXX without the message_thread_id parameter, making it impossible to diagnose from logs alone whether the threadId was passed correctly.
Suggested Fix
- Logging: Include
message_thread_id(if present) in thesendMessage oklog line - Root cause: Ensure the
threadIdfromrequesterOriginsurvives the full announce pipeline for Telegram DM topics (verify with a test case)
Environment
- OpenClaw version: latest (March 2026)
- Channel: Telegram (DM with bot topics enabled)
- Session type: main session (
agent:main:main) with DM topics