Skip to content

fix(subagent): announce direct-send path missing threadId for Telegram forum topics #21162

Description

@yhl999

Bug Description

Subagent completion announcements via sessions_spawn land in the General topic (Topic 1) of Telegram forum groups instead of the originating thread/topic.

Root Cause

In src/agents/subagent-announce.ts, the sendSubagentAnnounceDirectly() function has two delivery paths:

  1. Direct send path (used when expectsCompletionMessage: true, which is always the case for sessions_spawn):

    await callGateway({
      method: "send",
      params: {
        channel: completionChannel,
        to: completionTo,
        accountId: completionDirectOrigin?.accountId,
        // ❌ threadId is MISSING here
        sessionKey: canonicalRequesterSessionKey,
        message: params.completionMessage,
        idempotencyKey: params.directIdempotencyKey,
      },
    });
  2. Fallback agent path (correctly includes threadId):

    await callGateway({
      method: "agent",
      params: {
        // ...
        threadId: params.requesterIsSubagent ? undefined : threadId, // ✅ included
        // ...
      },
    });

The to field in session delivery context stores just the group ID (e.g., telegram:-100XXXXXXXXXX) without topic encoding, while threadId is stored as a separate field. Since the direct send path doesn't include threadId, Telegram delivers to the General topic.

Impact

  • All sessions_spawn completions in Telegram forum topic threads land in the wrong topic
  • The completionDirectOrigin object has threadId available but it's simply not being extracted/passed

Steps to Reproduce

  1. Set up a Telegram group with forum topics enabled
  2. Send a message in a non-General topic that triggers sessions_spawn
  3. Wait for the subagent to complete
  4. Observe: announcement appears in General topic instead of the originating topic

Fix

Extract threadId from completionDirectOrigin and pass it to the send call:

const completionThreadId =
  completionDirectOrigin?.threadId != null && completionDirectOrigin.threadId !== ""
    ? String(completionDirectOrigin.threadId)
    : undefined;
await callGateway({
  method: "send",
  params: {
    channel: completionChannel,
    to: completionTo,
    accountId: completionDirectOrigin?.accountId,
    threadId: completionThreadId,  // ← add this
    sessionKey: canonicalRequesterSessionKey,
    message: params.completionMessage,
    idempotencyKey: params.directIdempotencyKey,
  },
  timeoutMs: 15_000,
});

Environment

  • OpenClaw version: 2026.2.18
  • Platform: macOS (arm64)
  • Channel: Telegram (forum-enabled group)

Metadata

Metadata

Assignees

No one assigned

    Labels

    staleMarked as stale due to inactivity

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions