Skip to content

fix(slack): use threadId from delivery context as threadTs fallback in outbound adapter#22485

Closed
dorukardahan wants to merge 1 commit intoopenclaw:mainfrom
dorukardahan:fix/slack-outbound-threadid
Closed

fix(slack): use threadId from delivery context as threadTs fallback in outbound adapter#22485
dorukardahan wants to merge 1 commit intoopenclaw:mainfrom
dorukardahan:fix/slack-outbound-threadid

Conversation

@dorukardahan
Copy link
Contributor

@dorukardahan dorukardahan commented Feb 21, 2026

Summary

The Slack outbound adapter's sendText and sendMedia functions only used replyToId as the Slack thread_ts parameter. The delivery context also carries threadId (e.g. from subagent announce flows, gateway send RPC), but this field was never destructured or mapped to threadTs.

This caused subagent completion announcements (sessions_spawn) to land in the main Slack DM instead of the originating thread.

Root Cause

In extensions/slack/src/channel.ts, the outbound functions destructured replyToId but not threadId:

// Before
sendText: async ({ to, text, accountId, deps, replyToId, cfg }) => {
  const result = await send(to, text, {
    threadTs: replyToId ?? undefined,  // threadId ignored
  });
}

The delivery chain (deliverOutboundPayloadscreateChannelOutboundContextBaseresolveCtx) correctly propagates threadId through to the outbound adapter, but the Slack adapter never reads it.

Fix

Destructure threadId and use as fallback:

// After
sendText: async ({ to, text, accountId, deps, replyToId, threadId, cfg }) => {
  const result = await send(to, text, {
    threadTs: replyToId ?? threadId ?? undefined,
  });
}

Same for sendMedia.

Testing

  • Added debug logging to sendMessageSlack to trace threadTs value
  • Before fix: threadTs=NONE despite threadId being present in announce flow
  • After fix: threadTs=1771478217.865789 ✅ — announce lands in correct thread

Tested with multiple sessions_spawn calls from a Slack DM thread. All announcements now correctly route to the originating thread.

AI Disclosure

  • AI-assisted (Claude Opus 4.6 via OpenClaw)
  • Fully tested on live OpenClaw instance
  • I understand what the code does

Fixes #22483

Related: #17731, #22118

Greptile Summary

Fixes thread routing issue in Slack adapter by adding threadId as a fallback to replyToId when determining the Slack thread_ts parameter. This ensures subagent completion announcements and other flows that only provide threadId (not replyToId) correctly land in the originating thread rather than the main DM channel.

  • Destructures threadId parameter in both sendText and sendMedia functions
  • Uses fallback chain: replyToId ?? threadId ?? undefined for threadTs
  • Aligns Slack adapter with Telegram adapter pattern of supporting both fields
  • Tested with sessions_spawn flows to verify announcements now route to correct thread

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The change is minimal, well-targeted, and follows existing patterns in the codebase. It adds threadId as a fallback parameter (already present in the type definition) using a simple nullish coalescing chain. The Telegram adapter uses the same pattern, confirming this is the correct approach. The author has tested the fix with live sessions_spawn calls and confirmed the thread routing now works correctly. No breaking changes or side effects expected.
  • No files require special attention

Last reviewed commit: 289c9a9

(4/5) You can add custom instructions or style guidelines for the agent here!

…n outbound adapter

The Slack outbound adapter's sendText and sendMedia functions only used
replyToId as the Slack thread_ts parameter. The delivery context also
carries threadId (e.g. from subagent announce flows, gateway send RPC),
but this was never destructured or used.

This caused subagent completion announcements to land in the main DM
instead of the originating Slack thread — the threadId was correctly
propagated through the entire announce pipeline but silently dropped
at the final Slack delivery step.

Fix: destructure threadId from the outbound context and use it as a
fallback when replyToId is not set.

Fixes openclaw#22483
@openclaw-barnacle openclaw-barnacle bot added channel: slack Channel integration: slack size: XS labels Feb 21, 2026
@dorukardahan
Copy link
Contributor Author

CI Note: The CI / check failure is a pre-existing oxfmt formatting issue in .github/workflows/ci.yml — also failing on main branch (run #22252331341). Unrelated to this PR's changes in extensions/slack/src/channel.ts.

@vincentkoc
Copy link
Member

Consolidating this into #23836 so we ship a single Slack extension threading fix with tests + changelog:

  • read action now forwards threadId
  • outbound send uses delivery-context threadId fallback

Closing this as superseded to keep review/merge clean.

@vincentkoc
Copy link
Member

Superseded by #23836.

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

Labels

channel: slack Channel integration: slack size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Slack outbound adapter ignores threadId from delivery context — subagent announces land in main DM

2 participants