Runbook: Slack production troubleshooting (per-org / per-workspace)
Scope. Extension of slack-oauth-install.md. That runbook stands up the OAuth install; this one is for when a specific org's Slack install misbehaves in prod โ "we installed but nothing happens", "connect fails", "replies don't come back". It's the concrete log keywords + DB queries + Sentry filters to trace one org's Slack activity, distilled from the #3630 investigation.
Inputs you needโ
Two identifiers anchor every search below:
| Input | Where to get it | Example |
|---|---|---|
org_id (org UUID) | client URL / ops / the SQL in step 1 | f628ef6c-3faf-4fcd-b71a-e0af885e0665 |
team_id (Slack workspace id, Tโฆ) | step 1 (integration_credentials.team_id) or the Slack event body | T0AQDCQ0U14 |
org_id and team_id are the only identifiers that appear in plaintext across logs / DB / Sentry โ use them as the search keys.
1. DB โ the binding itselfโ
Does this org have an active Slack workspace, and which one?
SELECT *
FROM integration_credentials
WHERE integration_type = 'slack'
AND org_id = '<ORG_ID>';
- Expect
status = 'active'.status = 'token_invalid'= token revoked / 401 (workspace disconnected). team_id= the bound workspace;metadatacarriesteam_name/app_id/bot_user_id.- โ ๏ธ
SELECT *returnsencrypted_config(the encrypted bot token). It's ciphertext, but don't paste results into tickets/chat. Narrow the column list if you only need status.
Reverse โ which org holds a given workspace? (the "already connected to another organization" case, #3630)
SELECT org_id, status, team_id, metadata->>'team_name' AS team_name, created_at
FROM integration_credentials
WHERE integration_type = 'slack' AND team_id = '<TEAM_ID>';
-- team_id is unique (uq_integration_slack_team) โ at most one row.
-- An active + healthy row here means the workspace legitimately belongs to that
-- org โ NOT a stale binding. Do not "clean it up" without a business decision.
Which channels are bound, to which Specialist?
SELECT org_id, external_channel_id, specialist_id, bound_by_slack_user,
respond_to_all, created_at
FROM slack_channel_bindings
WHERE org_id = '<ORG_ID>'
ORDER BY created_at DESC;
Conversations produced via Slack:
SELECT id, specialist_id, channel, channel_thread_id, created_at
FROM conversations
WHERE org_id = '<ORG_ID>' AND channel = 'slack'
ORDER BY created_at DESC
LIMIT 50;
Outbound failures / DLQ (replies not going back):
SELECT id, channel, org_id, status, retry_count, error, payload, created_at
FROM channel_failures
WHERE channel = 'slack' AND org_id = '<ORG_ID>'
ORDER BY created_at DESC;
-- status: queued | acknowledged | drained; error often token_revoked / not_in_channel.
Optional, for "message arrived but bot didn't respond": slack_engaged_threads (mention gate) and channel_context_messages (channel_type = 'slack').
2. Logs โ keywords by lifecycle phaseโ
Prod app logs (ECS/CloudWatch). Channel IDs are masked (maskIdentifier), but org= and team= are plaintext โ search on those.
Install / OAuth callbackโ
| Meaning | Keyword |
|---|---|
Install succeeded (credential written active) | Slack app installed: org=<ORG_ID> team=<TEAM_ID> (slack-oauth.service.ts:378) |
| Org already bound to another workspace | Slack install refused: org= |
| Workspace already owned by another org (#3630) | Slack install refused: team= |
| Token exchange failed (bad secret / redirect mismatch) | Slack oauth.v2.access not ok: ยท Slack oauth.v2.access threw: |
| Callback error / conflict | Slack OAuth callback failed: ยท ... conflict: ยท ... taken: |
The client-side success signal is the redirect
?slack=connected&team=โฆ(browser only โ not in backend logs). Install success in logs = theSlack app installed:line, which corresponds to thestatus='active'row in step 1.
Inbound routingโ
| Meaning | Keyword |
|---|---|
Every inbound event (carries team=) | Slack event: type=<...> team=<TEAM_ID> |
| Routed to a conversation | Slack message routed: conv= |
| Deduped (vendor re-delivery) | Slack deduped: event_ts= |
| Held for Expert review (default HITL) | Slack reply held for expert review: conv= |
| Routing error | Slack routing error: |
Channel bindingโ
| Meaning | Keyword |
|---|---|
| Invited but org has no Specialist (no welcome, no bind) | Slack channel bind skipped: org=<ORG_ID> has no assigned Specialist (slack-channel-binding.service.ts:67) |
| Bot removed / channel unbound | Slack channel unbound: org=<ORG_ID> channel= |
Outboundโ
| Meaning | Keyword |
|---|---|
| Reply posted / dispatched | Slack reply posted to channel= ยท Slack reply dispatched: channel= |
| Workspace disconnected / bot removed (permanent) | token_revoked ยท token_invalid ยท channel_not_found ยท not_in_channel (slack-errors.util.ts) |
3. Sentry โ filter by org / workspace / channelโ
Org humanity-0r, EU region de.sentry.io, prod project h-work-production.
As of #3721 / PR #3724, channel events are tagged with org_id / team_id / channel / specialist_id, so these filters work directly in Sentry:
org_id:<ORG_ID>
team_id:<TEAM_ID>
channel:slack
Before #3721 is deployed, those tags are empty โ Sentry can only tell you a workspace erred, not which org's. In that case fall back to logs (
team=<TEAM_ID>) + the DB in steps 1โ2. After deploy, verify the tags exist before trusting the filter.
Triage orderโ
- Step 1 query โ confirm
status='active'andteam_idmatches. Wrong/absent โ install problem (see slack-oauth-install.md Part A/B). - Installed but org gets nothing โ grep logs
Slack event: โฆ team=<TEAM_ID>.- No such lines โ the workspace has sent no events: bot never invited to a channel / never DM'd. Installed but never used (the #3630 "HI workspace" state).
- Lines present but no conversation โ check
slack_channel_bindings(step 1) +Slack channel bind skipped โฆ no assigned Specialist.
- Replies not going out โ
channel_failures(step 1) + outbound permanent-error keywords (token_revoked/not_in_channel). - Cross-org "already connected" (#3630) โ the reverse
team_idquery in step 1 tells you the owning org; it is not necessarily stale.
Relatedโ
- slack-oauth-install.md โ the install runbook this extends.
- #3630 โ cross-org workspace-binding investigation that motivated this doc.
- #3721 / PR #3724 โ adds the
org_id/team_id/channelSentry tags used in step 3.