Skip to content

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

@yhl999

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions