Budibase/budibase · error · HTTPError

Failed to exchange Slack OAuth code

Error message

Failed to exchange Slack OAuth code

What it means

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.

Source

Thrown at packages/server/src/sdk/workspace/ai/deployments/slack.ts:242

  clientId: string
  clientSecret: string
  redirectUri: string
}) => {
  const response = await fetch(`${SLACK_API_BASE}/oauth.v2.access`, {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
    },
    body: new URLSearchParams({
      code,
      client_id: clientId,
      client_secret: clientSecret,
      redirect_uri: redirectUri,
    }),
  })
  const payload = (await response.json()) as SlackOAuthAccessResponse
  if (!response.ok) {
    throw new HTTPError("Failed to exchange Slack OAuth code", response.status)
  }
  return assertSlackOk(payload, "Failed to exchange Slack OAuth code")
}

export const rotateSlackConfigToken = async ({
  refreshToken,
}: {
  refreshToken: string
}) => {
  const response = await fetch(`${SLACK_API_BASE}/tooling.tokens.rotate`, {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
    },
    body: new URLSearchParams({
      refresh_token: refreshToken,
    }),
  })

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Verify client_id and client_secret match the Slack app's 'Basic Information' credentials exactly.
  2. Ensure redirect_uri is byte-for-byte identical to one of the redirect URLs registered in the Slack app's OAuth settings.
  3. Restart the OAuth flow so the user authorizes again and a fresh single-use code is issued.
  4. If the status is 5xx, retry after a short delay (transient Slack issue).

Example fix

// before
await exchangeSlackOAuthCode({ code, clientId, clientSecret, redirectUri })
// after - make sure redirectUri matches Slack app settings exactly
await exchangeSlackOAuthCode({
  code,
  clientId: process.env.SLACK_CLIENT_ID,
  clientSecret: process.env.SLACK_CLIENT_SECRET,
  redirectUri: "https://your-host.example.com/api/webhooks/slack/oauth/callback"
})
Defensive patterns

Strategy: validation

Validate before calling

if (!clientId || !clientSecret) throw new Error("Slack client credentials missing")
if (redirectUri !== REGISTERED_SLACK_REDIRECT_URI) {
  throw new Error("redirect_uri must exactly match a redirect URL registered in the Slack app")
}

Try / catch

try {
  const tokens = await exchangeSlackOAuthCode({ code, clientId, clientSecret, redirectUri })
} catch (e) {
  if (e instanceof HTTPError && e.status === 400) {
    // verify credentials/redirect_uri, then restart OAuth flow for a fresh code
  }
  throw e
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29). Data as JSON: /api/errors/165a33db77a37c58. Report an issue: GitHub.