{"record":{"id":"165a33db77a37c58","repo":"Budibase/budibase","slug":"failed-to-exchange-slack-oauth-code","errorCode":null,"errorMessage":"Failed to exchange Slack OAuth code","messagePattern":"Failed to exchange Slack OAuth code","errorType":"http","errorClass":"HTTPError","httpStatus":null,"severity":"error","filePath":"packages/server/src/sdk/workspace/ai/deployments/slack.ts","lineNumber":242,"sourceCode":"  clientId: string\n  clientSecret: string\n  redirectUri: string\n}) => {\n  const response = await fetch(`${SLACK_API_BASE}/oauth.v2.access`, {\n    method: \"POST\",\n    headers: {\n      \"Content-Type\": \"application/x-www-form-urlencoded\",\n    },\n    body: new URLSearchParams({\n      code,\n      client_id: clientId,\n      client_secret: clientSecret,\n      redirect_uri: redirectUri,\n    }),\n  })\n  const payload = (await response.json()) as SlackOAuthAccessResponse\n  if (!response.ok) {\n    throw new HTTPError(\"Failed to exchange Slack OAuth code\", response.status)\n  }\n  return assertSlackOk(payload, \"Failed to exchange Slack OAuth code\")\n}\n\nexport const rotateSlackConfigToken = async ({\n  refreshToken,\n}: {\n  refreshToken: string\n}) => {\n  const response = await fetch(`${SLACK_API_BASE}/tooling.tokens.rotate`, {\n    method: \"POST\",\n    headers: {\n      \"Content-Type\": \"application/x-www-form-urlencoded\",\n    },\n    body: new URLSearchParams({\n      refresh_token: refreshToken,\n    }),\n  })","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/ai/deployments/slack.ts#L224-L260","documentation":"This error is thrown by exchangeSlackOAuthCode when Slack's oauth.v2.access endpoint responds with a non-ok HTTP status during authorization code exchange. The code exchange turns a temporary OAuth code into bot tokens using client_id/client_secret/redirect_uri; an HTTP-level failure means the request was rejected before Slack returned a structured ok:false payload.","triggerScenarios":"POSTing the OAuth code exchange to Slack and receiving 4xx/5xx - e.g. wrong client credentials causing 400 invalid_client, a redirect_uri that does not exactly match the registered request URI, or network-level 5xx from Slack.","commonSituations":"redirect_uri mismatch between the Budibase OAuth initiation and the Slack app's registered redirect URLs; copy-paste errors in client_id/client_secret; exchanging a code twice (second attempt may fail at HTTP level depending on timing); Slack outage.","solutions":["Verify client_id and client_secret match the Slack app's 'Basic Information' credentials exactly.","Ensure redirect_uri is byte-for-byte identical to one of the redirect URLs registered in the Slack app's OAuth settings.","Restart the OAuth flow so the user authorizes again and a fresh single-use code is issued.","If the status is 5xx, retry after a short delay (transient Slack issue)."],"exampleFix":"// before\nawait exchangeSlackOAuthCode({ code, clientId, clientSecret, redirectUri })\n// after - make sure redirectUri matches Slack app settings exactly\nawait exchangeSlackOAuthCode({\n  code,\n  clientId: process.env.SLACK_CLIENT_ID,\n  clientSecret: process.env.SLACK_CLIENT_SECRET,\n  redirectUri: \"https://your-host.example.com/api/webhooks/slack/oauth/callback\"\n})","handlingStrategy":"validation","validationCode":"if (!clientId || !clientSecret) throw new Error(\"Slack client credentials missing\")\nif (redirectUri !== REGISTERED_SLACK_REDIRECT_URI) {\n  throw new Error(\"redirect_uri must exactly match a redirect URL registered in the Slack app\")\n}","typeGuard":null,"tryCatchPattern":"try {\n  const tokens = await exchangeSlackOAuthCode({ code, clientId, clientSecret, redirectUri })\n} catch (e) {\n  if (e instanceof HTTPError && e.status === 400) {\n    // verify credentials/redirect_uri, then restart OAuth flow for a fresh code\n  }\n  throw e\n}","preventionTips":["Keep the redirect_uri in one shared constant that matches the Slack app configuration exactly.","Never reuse OAuth codes; start a new authorization when in doubt.","Load client_id/client_secret from env/secret manager and verify at startup."],"tags":["slack","oauth","http","redirect-uri"],"backgroundTag":"oauth-code-exchange-failed","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}