Skip to content

fix(slack): pass recipient_team_id to streaming API calls#20988

Merged
vincentkoc merged 3 commits intoopenclaw:mainfrom
Dithilli:fix/slack-streaming-team-id
Feb 19, 2026
Merged

fix(slack): pass recipient_team_id to streaming API calls#20988
vincentkoc merged 3 commits intoopenclaw:mainfrom
Dithilli:fix/slack-streaming-team-id

Conversation

@Dithilli
Copy link
Contributor

@Dithilli Dithilli commented Feb 19, 2026

Summary

Fix Slack native streaming (chat.startStream/stopStream) which was broken by two issues:

  1. Missing API parameters: Pass recipient_team_id and recipient_user_id to the ChatStreamer to fix missing_recipient_team_id / missing_recipient_user_id errors.
  2. Block streaming pipeline conflict: Disable the app-level block streaming pipeline when native streaming is active, which was intercepting and dropping replies before they could reach the Slack streaming API.

Problem

Missing recipient IDs

Slack's Agents & AI Apps streaming API requires recipient_team_id and recipient_user_id parameters. Without them, stopStream fails with:

[slack] slack-stream: failed to stop stream: Error: An API error occurred: missing_recipient_team_id

or in DM contexts:

[slack] slack-stream: failed to stop stream: Error: An API error occurred: missing_recipient_user_id

Block streaming conflict

When native streaming was enabled, disableBlockStreaming was set to false, which activated the app-level block streaming pipeline. This pipeline:

  1. Intercepted agent output as "block" replies during the run
  2. Sent them through onBlockReplysendBlockReply in the dispatcher
  3. Dropped the final payloads (to avoid duplicates with already-streamed blocks)

But the final payloads are what would flow through deliverWithStreaming → Slack's native streaming API. Result: zero replies delivered through either path.

Fix

Commit 1: Pass recipient IDs to streaming API

The team ID is already fetched at provider startup via auth.test and stored in the monitor context (ctx.teamId). The user ID comes from the incoming message (message.user). This commit:

  1. Adds optional teamId and userId parameters to StartSlackStreamParams
  2. Passes them as recipient_team_id and recipient_user_id to client.chatStream()
  3. Threads ctx.teamId and message.user through from the dispatch call site

Commit 2: Disable block streaming when native streaming is active

Sets disableBlockStreaming: true when useStreaming is true, so the final reply flows through deliverWithStreamingstartSlackStream/appendStream/stopStream as intended, without the block streaming pipeline intercepting it.

Testing

  • pnpm build — passes
  • pnpm test -- --run src/slack/monitor/message-handler/dispatch.streaming.test.ts — 5/5 pass
  • Live Slack workspace verification — replies delivered with streaming enabled

Related Issues

Fixes #19839 — Slack native streaming: missing_recipient_team_id error on chat.stopStream
Fixes #20847 — Slack: missing_recipient_team_id error on workspaces with Slack Connect enabled
Fixes #20299 — Slack: missing_recipient_team_id error when stopping typing indicator stream
Fixes #19791 — Slack block streaming fails with missing_recipient_team_id in multi-account setup
Fixes #20337 — Slack thread replies can fail when streaming is enabled

AI Disclosure

  • AI-assisted (Claude Opus 4.6 via OpenClaw)
  • Live tested on a Slack workspace with streaming enabled
  • Both fixes verified: recipient IDs resolve the API errors, disabling block streaming ensures replies flow through the native streaming path

Greptile Summary

Adds missing recipient_team_id and recipient_user_id parameters to Slack's native streaming API calls and fixes a conflict where block streaming was intercepting replies meant for the native streaming pipeline.

Changes:

  • Passes ctx.teamId and message.user to startSlackStream() in dispatch.ts:202-203, which are then forwarded as recipient_team_id and recipient_user_id to the Slack API
  • Changes disableBlockStreaming from false to true when native streaming is active (dispatch.ts:359), preventing the block streaming pipeline from intercepting final replies
  • Adds optional teamId and userId parameters to StartSlackStreamParams interface with clear documentation
  • Updates startSlackStream() to conditionally include these parameters in the client.chatStream() call

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The changes are straightforward and well-targeted. The fix passes required parameters (teamId from ctx.teamId which is a required field in SlackMonitorContext, and userId from message.user) to the Slack streaming API. The logic change for disableBlockStreaming correctly prevents pipeline conflicts. The implementation includes proper optional parameter handling with conditional spreading, comprehensive JSDoc comments, and has passing tests. The changes directly address the documented API errors.
  • No files require special attention

Last reviewed commit: 48195a4

(3/5) Reply to the agent's comments like "Can you suggest a fix for this @greptileai?" or ask follow-up questions!

@openclaw-barnacle openclaw-barnacle bot added channel: slack Channel integration: slack size: XS labels Feb 19, 2026
… API calls

The Slack Agents & AI Apps streaming API (chat.startStream / chat.stopStream)
requires recipient_team_id and recipient_user_id parameters. Without them,
stopStream fails with 'missing_recipient_team_id' (all contexts) or
'missing_recipient_user_id' (DM contexts), causing streamed messages to
disappear after generation completes.

This passes:
- team_id (from auth.test at provider startup, stored in monitor context)
- user_id (from the incoming message sender, for DM recipient identification)

through to the ChatStreamer via recipient_team_id and recipient_user_id options.

Fixes openclaw#19839, openclaw#20847, openclaw#20299, openclaw#19791, openclaw#20337

AI-assisted: Written with Claude (Opus 4.6) via OpenClaw. Lightly tested
(unit tests pass, live workspace verification in progress).
@Dithilli Dithilli force-pushed the fix/slack-streaming-team-id branch from b55e0fa to f0bf3be Compare February 19, 2026 14:46
@Dithilli
Copy link
Contributor Author

Live verification complete

Tested on a standard (non-Enterprise) Slack workspace with socket mode:

  • Before fix: missing_recipient_team_id on every stopStream call, messages disappearing or failing silently
  • After adding recipient_team_id: error changed to missing_recipient_user_id (DMs only)
  • After adding both recipient_team_id + recipient_user_id: no streaming errors, DMs and channel messages both working correctly with native streaming enabled

Streaming was disabled as a workaround for ~13 days. Re-enabled with this fix, no issues.

AI-assisted: Claude (Opus 4.6) via OpenClaw

@Dithilli
Copy link
Contributor Author

CI note: The check job failure is a pre-existing oxfmt formatting issue in src/channels/plugins/message-actions.security.test.ts and src/channels/plugins/message-actions.ts — files not touched by this PR. These same files fail formatting on the current main branch.

Our changes are limited to src/slack/streaming.ts and src/slack/monitor/message-handler/dispatch.ts only.

When Slack native streaming (`chat.startStream`/`stopStream`) is enabled,
`disableBlockStreaming` was set to `false`, which activated the app-level
block streaming pipeline. This pipeline intercepted agent output, sent it
via block replies, then dropped the final payloads that would have flowed
through `deliverWithStreaming` to the Slack streaming API — resulting in
zero replies delivered.

Set `disableBlockStreaming: true` when native streaming is active so the
final reply flows through the Slack streaming API path as intended.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vincentkoc
Copy link
Member

Credits: Main implementation is in this PR by @Dithilli. The earlier recipient-ID fix path and groundwork were contributed in #20377 by @AsserAl1012, and that contribution should be preserved in final credit attribution.

@mattmiddlesworth
Copy link

following for this fix to be merged in. Thanks for working on it!

@vincentkoc
Copy link
Member

@greptileai review

@vincentkoc vincentkoc merged commit bbcb3ac into openclaw:main Feb 19, 2026
25 checks passed
vignesh07 pushed a commit to pahdo/openclaw that referenced this pull request Feb 20, 2026
…0988)

* fix(slack): pass recipient_team_id and recipient_user_id to streaming API calls

The Slack Agents & AI Apps streaming API (chat.startStream / chat.stopStream)
requires recipient_team_id and recipient_user_id parameters. Without them,
stopStream fails with 'missing_recipient_team_id' (all contexts) or
'missing_recipient_user_id' (DM contexts), causing streamed messages to
disappear after generation completes.

This passes:
- team_id (from auth.test at provider startup, stored in monitor context)
- user_id (from the incoming message sender, for DM recipient identification)

through to the ChatStreamer via recipient_team_id and recipient_user_id options.

Fixes openclaw#19839, openclaw#20847, openclaw#20299, openclaw#19791, openclaw#20337

AI-assisted: Written with Claude (Opus 4.6) via OpenClaw. Lightly tested
(unit tests pass, live workspace verification in progress).

* fix(slack): disable block streaming when native streaming is active

When Slack native streaming (`chat.startStream`/`stopStream`) is enabled,
`disableBlockStreaming` was set to `false`, which activated the app-level
block streaming pipeline. This pipeline intercepted agent output, sent it
via block replies, then dropped the final payloads that would have flowed
through `deliverWithStreaming` to the Slack streaming API — resulting in
zero replies delivered.

Set `disableBlockStreaming: true` when native streaming is active so the
final reply flows through the Slack streaming API path as intended.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
anisoptera pushed a commit to anisoptera/openclaw that referenced this pull request Feb 20, 2026
…0988)

* fix(slack): pass recipient_team_id and recipient_user_id to streaming API calls

The Slack Agents & AI Apps streaming API (chat.startStream / chat.stopStream)
requires recipient_team_id and recipient_user_id parameters. Without them,
stopStream fails with 'missing_recipient_team_id' (all contexts) or
'missing_recipient_user_id' (DM contexts), causing streamed messages to
disappear after generation completes.

This passes:
- team_id (from auth.test at provider startup, stored in monitor context)
- user_id (from the incoming message sender, for DM recipient identification)

through to the ChatStreamer via recipient_team_id and recipient_user_id options.

Fixes openclaw#19839, openclaw#20847, openclaw#20299, openclaw#19791, openclaw#20337

AI-assisted: Written with Claude (Opus 4.6) via OpenClaw. Lightly tested
(unit tests pass, live workspace verification in progress).

* fix(slack): disable block streaming when native streaming is active

When Slack native streaming (`chat.startStream`/`stopStream`) is enabled,
`disableBlockStreaming` was set to `false`, which activated the app-level
block streaming pipeline. This pipeline intercepted agent output, sent it
via block replies, then dropped the final payloads that would have flowed
through `deliverWithStreaming` to the Slack streaming API — resulting in
zero replies delivered.

Set `disableBlockStreaming: true` when native streaming is active so the
final reply flows through the Slack streaming API path as intended.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
rodrigogs pushed a commit to rodrigogs/openclaw that referenced this pull request Feb 20, 2026
…0988)

* fix(slack): pass recipient_team_id and recipient_user_id to streaming API calls

The Slack Agents & AI Apps streaming API (chat.startStream / chat.stopStream)
requires recipient_team_id and recipient_user_id parameters. Without them,
stopStream fails with 'missing_recipient_team_id' (all contexts) or
'missing_recipient_user_id' (DM contexts), causing streamed messages to
disappear after generation completes.

This passes:
- team_id (from auth.test at provider startup, stored in monitor context)
- user_id (from the incoming message sender, for DM recipient identification)

through to the ChatStreamer via recipient_team_id and recipient_user_id options.

Fixes openclaw#19839, openclaw#20847, openclaw#20299, openclaw#19791, openclaw#20337

AI-assisted: Written with Claude (Opus 4.6) via OpenClaw. Lightly tested
(unit tests pass, live workspace verification in progress).

* fix(slack): disable block streaming when native streaming is active

When Slack native streaming (`chat.startStream`/`stopStream`) is enabled,
`disableBlockStreaming` was set to `false`, which activated the app-level
block streaming pipeline. This pipeline intercepted agent output, sent it
via block replies, then dropped the final payloads that would have flowed
through `deliverWithStreaming` to the Slack streaming API — resulting in
zero replies delivered.

Set `disableBlockStreaming: true` when native streaming is active so the
final reply flows through the Slack streaming API path as intended.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Hansen1018 added a commit to Hansen1018/openclaw that referenced this pull request Feb 21, 2026
…0988)

* fix(slack): pass recipient_team_id and recipient_user_id to streaming API calls

The Slack Agents & AI Apps streaming API (chat.startStream / chat.stopStream)
requires recipient_team_id and recipient_user_id parameters. Without them,
stopStream fails with 'missing_recipient_team_id' (all contexts) or
'missing_recipient_user_id' (DM contexts), causing streamed messages to
disappear after generation completes.

This passes:
- team_id (from auth.test at provider startup, stored in monitor context)
- user_id (from the incoming message sender, for DM recipient identification)

through to the ChatStreamer via recipient_team_id and recipient_user_id options.

Fixes openclaw#19839, openclaw#20847, openclaw#20299, openclaw#19791, openclaw#20337

AI-assisted: Written with Claude (Opus 4.6) via OpenClaw. Lightly tested
(unit tests pass, live workspace verification in progress).

* fix(slack): disable block streaming when native streaming is active

When Slack native streaming (`chat.startStream`/`stopStream`) is enabled,
`disableBlockStreaming` was set to `false`, which activated the app-level
block streaming pipeline. This pipeline intercepted agent output, sent it
via block replies, then dropped the final payloads that would have flowed
through `deliverWithStreaming` to the Slack streaming API — resulting in
zero replies delivered.

Set `disableBlockStreaming: true` when native streaming is active so the
final reply flows through the Slack streaming API path as intended.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
vincentkoc added a commit that referenced this pull request Feb 21, 2026
* fix(slack): pass recipient_team_id and recipient_user_id to streaming API calls

The Slack Agents & AI Apps streaming API (chat.startStream / chat.stopStream)
requires recipient_team_id and recipient_user_id parameters. Without them,
stopStream fails with 'missing_recipient_team_id' (all contexts) or
'missing_recipient_user_id' (DM contexts), causing streamed messages to
disappear after generation completes.

This passes:
- team_id (from auth.test at provider startup, stored in monitor context)
- user_id (from the incoming message sender, for DM recipient identification)

through to the ChatStreamer via recipient_team_id and recipient_user_id options.

Fixes #19839, #20847, #20299, #19791, #20337

AI-assisted: Written with Claude (Opus 4.6) via OpenClaw. Lightly tested
(unit tests pass, live workspace verification in progress).

* fix(slack): disable block streaming when native streaming is active

When Slack native streaming (`chat.startStream`/`stopStream`) is enabled,
`disableBlockStreaming` was set to `false`, which activated the app-level
block streaming pipeline. This pipeline intercepted agent output, sent it
via block replies, then dropped the final payloads that would have flowed
through `deliverWithStreaming` to the Slack streaming API — resulting in
zero replies delivered.

Set `disableBlockStreaming: true` when native streaming is active so the
final reply flows through the Slack streaming API path as intended.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
vincentkoc added a commit that referenced this pull request Feb 21, 2026
* fix(slack): pass recipient_team_id and recipient_user_id to streaming API calls

The Slack Agents & AI Apps streaming API (chat.startStream / chat.stopStream)
requires recipient_team_id and recipient_user_id parameters. Without them,
stopStream fails with 'missing_recipient_team_id' (all contexts) or
'missing_recipient_user_id' (DM contexts), causing streamed messages to
disappear after generation completes.

This passes:
- team_id (from auth.test at provider startup, stored in monitor context)
- user_id (from the incoming message sender, for DM recipient identification)

through to the ChatStreamer via recipient_team_id and recipient_user_id options.

Fixes #19839, #20847, #20299, #19791, #20337

AI-assisted: Written with Claude (Opus 4.6) via OpenClaw. Lightly tested
(unit tests pass, live workspace verification in progress).

* fix(slack): disable block streaming when native streaming is active

When Slack native streaming (`chat.startStream`/`stopStream`) is enabled,
`disableBlockStreaming` was set to `false`, which activated the app-level
block streaming pipeline. This pipeline intercepted agent output, sent it
via block replies, then dropped the final payloads that would have flowed
through `deliverWithStreaming` to the Slack streaming API — resulting in
zero replies delivered.

Set `disableBlockStreaming: true` when native streaming is active so the
final reply flows through the Slack streaming API path as intended.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
mmyyfirstb pushed a commit to mmyyfirstb/openclaw that referenced this pull request Feb 21, 2026
…0988)

* fix(slack): pass recipient_team_id and recipient_user_id to streaming API calls

The Slack Agents & AI Apps streaming API (chat.startStream / chat.stopStream)
requires recipient_team_id and recipient_user_id parameters. Without them,
stopStream fails with 'missing_recipient_team_id' (all contexts) or
'missing_recipient_user_id' (DM contexts), causing streamed messages to
disappear after generation completes.

This passes:
- team_id (from auth.test at provider startup, stored in monitor context)
- user_id (from the incoming message sender, for DM recipient identification)

through to the ChatStreamer via recipient_team_id and recipient_user_id options.

Fixes openclaw#19839, openclaw#20847, openclaw#20299, openclaw#19791, openclaw#20337

AI-assisted: Written with Claude (Opus 4.6) via OpenClaw. Lightly tested
(unit tests pass, live workspace verification in progress).

* fix(slack): disable block streaming when native streaming is active

When Slack native streaming (`chat.startStream`/`stopStream`) is enabled,
`disableBlockStreaming` was set to `false`, which activated the app-level
block streaming pipeline. This pipeline intercepted agent output, sent it
via block replies, then dropped the final payloads that would have flowed
through `deliverWithStreaming` to the Slack streaming API — resulting in
zero replies delivered.

Set `disableBlockStreaming: true` when native streaming is active so the
final reply flows through the Slack streaming API path as intended.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
obviyus pushed a commit to guirguispierre/openclaw that referenced this pull request Feb 22, 2026
…0988)

* fix(slack): pass recipient_team_id and recipient_user_id to streaming API calls

The Slack Agents & AI Apps streaming API (chat.startStream / chat.stopStream)
requires recipient_team_id and recipient_user_id parameters. Without them,
stopStream fails with 'missing_recipient_team_id' (all contexts) or
'missing_recipient_user_id' (DM contexts), causing streamed messages to
disappear after generation completes.

This passes:
- team_id (from auth.test at provider startup, stored in monitor context)
- user_id (from the incoming message sender, for DM recipient identification)

through to the ChatStreamer via recipient_team_id and recipient_user_id options.

Fixes openclaw#19839, openclaw#20847, openclaw#20299, openclaw#19791, openclaw#20337

AI-assisted: Written with Claude (Opus 4.6) via OpenClaw. Lightly tested
(unit tests pass, live workspace verification in progress).

* fix(slack): disable block streaming when native streaming is active

When Slack native streaming (`chat.startStream`/`stopStream`) is enabled,
`disableBlockStreaming` was set to `false`, which activated the app-level
block streaming pipeline. This pipeline intercepted agent output, sent it
via block replies, then dropped the final payloads that would have flowed
through `deliverWithStreaming` to the Slack streaming API — resulting in
zero replies delivered.

Set `disableBlockStreaming: true` when native streaming is active so the
final reply flows through the Slack streaming API path as intended.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
zooqueen pushed a commit to hanzoai/bot that referenced this pull request Mar 6, 2026
…0988)

* fix(slack): pass recipient_team_id and recipient_user_id to streaming API calls

The Slack Agents & AI Apps streaming API (chat.startStream / chat.stopStream)
requires recipient_team_id and recipient_user_id parameters. Without them,
stopStream fails with 'missing_recipient_team_id' (all contexts) or
'missing_recipient_user_id' (DM contexts), causing streamed messages to
disappear after generation completes.

This passes:
- team_id (from auth.test at provider startup, stored in monitor context)
- user_id (from the incoming message sender, for DM recipient identification)

through to the ChatStreamer via recipient_team_id and recipient_user_id options.

Fixes openclaw#19839, openclaw#20847, openclaw#20299, openclaw#19791, openclaw#20337

AI-assisted: Written with Claude (Opus 4.6) via OpenClaw. Lightly tested
(unit tests pass, live workspace verification in progress).

* fix(slack): disable block streaming when native streaming is active

When Slack native streaming (`chat.startStream`/`stopStream`) is enabled,
`disableBlockStreaming` was set to `false`, which activated the app-level
block streaming pipeline. This pipeline intercepted agent output, sent it
via block replies, then dropped the final payloads that would have flowed
through `deliverWithStreaming` to the Slack streaming API — resulting in
zero replies delivered.

Set `disableBlockStreaming: true` when native streaming is active so the
final reply flows through the Slack streaming API path as intended.


---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment