Skip to content

fix(slack): thread team_id to chat.startStream for Enterprise Grid (#95)#176

Merged
patrick-chinchill merged 1 commit into
mainfrom
fix/slack-95-grid-startstream-team-id
Jun 21, 2026
Merged

fix(slack): thread team_id to chat.startStream for Enterprise Grid (#95)#176
patrick-chinchill merged 1 commit into
mainfrom
fix/slack-95-grid-startstream-team-id

Conversation

@patrick-chinchill

Copy link
Copy Markdown
Collaborator

Summary

Fixes Slack issue #95chat.startStream fails with team_not_found on Enterprise Grid workspaces, even with a non-empty thread_ts. Non-streaming chat.postMessage succeeds on the same workspace; only chat.startStream regressed.

Root cause: On Grid orgs chat.startStream requires an explicit workspace team_id to disambiguate the team (the per-workspace bot token alone isn't sufficient). stream() built its chat_stream(...) kwargs from channel / thread_ts / recipient_user_id / recipient_team_id / task_display_mode and never passed a workspace team_id.

Fix

src/chat_sdk/adapters/slack/adapter.py stream() — thread team_id=options.recipient_team_id into the chat_stream(...) kwargs:

stream_kwargs: dict[str, Any] = {
    "channel": channel,
    "thread_ts": thread_ts,
    "recipient_user_id": options.recipient_user_id,
    "recipient_team_id": options.recipient_team_id,
    # ... Grid disambiguation (#95) ...
    "team_id": options.recipient_team_id,
}

recipient_team_id is the team.id extracted on the inbound path (the workspace where the interaction happened = the streaming-target workspace). Harmless on non-Grid workspaces — a correct team_id is always valid. The empty-thread_ts DM degrade-to-post_message path is unchanged.

slack_sdk investigation (how team_id reaches the API)

Verified against installed slack-sdk==3.42.0:

  • AsyncWebClient.chat_stream(...) has no team_id param of its own, but stashes unknown **kwargs into AsyncChatStream._stream_args.
  • _stream_args is forwarded only to the two chat.startStream call sites inside AsyncChatStream — the eager first-flush path (_flush_buffer) and the lazy stop-without-flush path (stop). Both use **self._stream_args.
  • chat.startStream / chat.appendStream / chat.stopStream all forward their **kwargs straight to api_call(..., json=kwargs), so team_id is a real, forwarded API param.
  • Conclusion: passing team_id to chat_stream(...) threads it through to chat.startStream (the only call that fails per Slack: chat.startStream fails with team_not_found on Enterprise Grid workspaces #95). chat.appendStream / chat.stopStream do not receive _stream_args and do not need team_id on Grid.

Tests

tests/test_slack_api.py::TestStream:

  • test_stream_threads_team_id_to_chat_stream_for_grid — simulates a Grid workspace: post_message succeeds with no team_id; the streamer's lazy chat.startStream raises team_not_found when team_id is absent and succeeds when present. Asserts the fix passes team_id == recipient_team_id to chat_stream. Mutation-proven: drop the team_id line and this test fails with team_not_found.
  • test_stream_raises_team_not_found_without_team_id_on_grid — mutation guard proving the Grid simulation isn't vacuous (stripping team_id always raises).

Both use a local _FakeSlackApiError stand-in (mirroring SlackApiError's .response dict + message) so they don't depend on slack_sdk being installed — the dev group doesn't pull it in, and test_slack_client_cache.py injects a bare slack_sdk ModuleType with no errors submodule under CI.

Divergence doc

Adds a Known Non-Parity row to docs/UPSTREAM_SYNC.md (cites #95): Grid chat.startStream needs team_id; we thread recipient_team_id as the workspace team. Upstream has the same gap — its stream() never threads team_id, so streaming is broken on Grid while non-streaming posts work.

Env limitation

Live Grid verification needs a real Enterprise Grid workspace; the unit test simulates it (raises team_not_found from the streamer's lazy chat.startStream when team_id is absent).

Also included

scripts/fidelity_baseline.json ts_parity refreshed chat@4.30.0 -> chat@4.31.0 (stale since the 0.4.31 release; content already 0-missing). This unblocks verify_test_fidelity.py's parity gate, which fails on any branch off current main.

Gauntlet

  • ruff check: OK
  • ruff format --check: OK
  • audit_test_quality.py: 0 hard failures
  • pyrefly check src/: 0 errors
  • verify_test_fidelity.py --strict (TS_ROOT=chat@4.31.0): 732/732 matched, 0 missing
  • pytest tests/: 5248 passed, 4 skipped (run with slack_sdk uninstalled to mirror CI)

chat.startStream fails with `team_not_found` on Enterprise Grid orgs
because the per-workspace bot token alone isn't enough to disambiguate
the team — Grid requires an explicit workspace `team_id`. Non-streaming
chat.postMessage succeeds without it, so only the streaming path
regressed: `stream()` built its `chat_stream(...)` kwargs from
channel / thread_ts / recipient_user_id / recipient_team_id /
task_display_mode and never passed a workspace `team_id`.

Fix: thread `team_id=options.recipient_team_id` into the chat_stream
call. slack_sdk's AsyncChatStream stashes unknown kwargs in
`_stream_args` and forwards them to BOTH chat.startStream call sites
(the eager first-flush path and the lazy stop-without-flush path), so
this reaches the only API call that needs it. chat.appendStream /
chat.stopStream don't receive `_stream_args` and don't require
`team_id`. recipient_team_id is the team.id extracted on the inbound
path (the workspace where the interaction happened = the streaming
target workspace). Harmless on non-Grid workspaces — a correct team_id
is always valid.

Tests: a Grid regression in tests/test_slack_api.py simulating an
Enterprise Grid workspace where post_message succeeds without team_id
but the streaming start raises team_not_found when team_id is absent
and succeeds when present; plus a mutation-guard test proving the
simulation fails without team_id. Both use a local SlackApiError
stand-in so they don't depend on slack_sdk being installed (the dev
group doesn't pull it in; a fake module is injected under CI).

Also refreshes scripts/fidelity_baseline.json ts_parity 4.30.0 -> 4.31.0
(stale since the 0.4.31 release; content already 0-missing) and adds a
Known Non-Parity row to docs/UPSTREAM_SYNC.md documenting that upstream
has the same Grid gap.

Live Grid verification needs a real Enterprise Grid workspace; the unit
test simulates it.
@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@patrick-chinchill, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 59 minutes and 56 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ba5f7a62-5de5-48c1-98e9-da611f9430b6

📥 Commits

Reviewing files that changed from the base of the PR and between 3d753c0 and b826204.

📒 Files selected for processing (4)
  • docs/UPSTREAM_SYNC.md
  • scripts/fidelity_baseline.json
  • src/chat_sdk/adapters/slack/adapter.py
  • tests/test_slack_api.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request resolves Enterprise Grid issue #95 by passing the workspace team_id (sourced from options.recipient_team_id) into client.chat_stream(...) in the Slack adapter, preventing team_not_found errors during streaming. The changes also include updating the upstream sync documentation, bumping the ts_parity version in the fidelity baseline to chat@4.31.0, and adding robust unit tests with a mock _FakeSlackApiError to verify the fix. There are no review comments, so I have no feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@patrick-chinchill patrick-chinchill marked this pull request as ready for review June 21, 2026 19:35
@patrick-chinchill patrick-chinchill merged commit 707b657 into main Jun 21, 2026
9 checks passed
patrick-chinchill added a commit that referenced this pull request Jun 21, 2026
Python-only patch on 4.31.0 (UPSTREAM_PARITY unchanged).
- Slack #138: empty-DM thread_ts fetch routes to conversations.history (PR #175)
- Slack #95: thread team_id to chat.startStream for Enterprise Grid (PR #176)
- docs: mark Linear agent-sessions mode experimental (unverified vs live tenant, #151)
- version 0.4.31 -> 0.4.31.1; CHANGELOG; CLAUDE.md mapping
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant