Budibase/budibase · error · Error

Slack OAuth response did not include a bot token

Error message

Slack OAuth response did not include a bot token

What it means

After exchanging the authorization code via sdk.ai.deployments.slack.exchangeSlackOAuthCode, the handler expects token.access_token (the Slack bot token, xoxb-...). If the exchange response has no access_token, it throws this Error since the agent cannot operate without a bot token.

Source

Thrown at packages/server/src/api/controllers/ai/agents.ts:612

  await context.doInWorkspaceContext(statePayload.workspaceId, async () => {
    const agent = await sdk.ai.agents.getOrThrow(statePayload.agentId)
    const clientId = agent.slackIntegration?.clientId?.trim()
    const clientSecret = agent.slackIntegration?.clientSecret?.trim()
    if (!clientId || !clientSecret) {
      throw new Error("Slack OAuth client credentials are not configured")
    }

    const redirectUri = await getSlackOAuthRedirectUrl()
    const token = await sdk.ai.deployments.slack.exchangeSlackOAuthCode({
      code,
      clientId,
      clientSecret,
      redirectUri,
    })
    const botToken = token.access_token?.trim()
    if (!botToken) {
      throw new Error("Slack OAuth response did not include a bot token")
    }

    const updatedAgent = await sdk.ai.agents.update({
      ...agent,
      slackIntegration: {
        ...agent.slackIntegration,
        appId: token.app_id?.trim() || agent.slackIntegration?.appId,
        botToken,
        botUserId: token.bot_user_id?.trim() || undefined,
        teamId: token.team?.id?.trim() || undefined,
        teamName: token.team?.name?.trim() || undefined,
      },
    })
    await publishSlackIntegrationForLiveAgent(updatedAgent)
  })

  ctx.redirect(
    `/builder/workspace/${statePayload.workspaceId}/agent/${statePayload.agentId}/deployment?slack_connected=1`

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Log the full exchange response — it usually contains an 'error' field (invalid_code, bad_redirect_uri, etc.) explaining the failure
  2. Restart the OAuth flow so a fresh, unused code is exchanged
  3. Ensure the redirectUri passed to exchangeSlackOAuthCode exactly matches the one used in the authorize URL and Slack app settings
  4. Verify clientId/clientSecret are correct and the Slack app has bot token scopes configured
Defensive patterns

Strategy: try-catch

Validate before calling

const token = await sdk.ai.deployments.slack.exchangeSlackOAuthCode({ code, clientId, clientSecret, redirectUri })
if (!token.access_token?.trim()) {
  // inspect token.error (e.g. invalid_code, bad_redirect_uri) before updating the agent
  console.error("Slack token exchange failed:", token)
}

Type guard

function hasBotToken(t: { access_token?: string }): t is { access_token: string } {
  return typeof t.access_token === "string" && t.access_token.trim() !== ""
}

Try / catch

try {
  await completeSlackOAuth(ctx)
} catch (err) {
  if (err.message.includes("did not include a bot token")) {
    // log exchange response error field, restart OAuth with a fresh code and matching redirect_uri
  } else { throw err }
}

Prevention

When it happens

Trigger: Slack's oauth.v2.access response lacks access_token — the code was invalid/already used (Slack returns ok:false with an error field instead), wrong clientId/clientSecret, redirect_uri mismatch with the one used at authorize time, or missing scope grant for a bot token.

Common situations: Replayed/expired authorization code; redirect_uri passed to the exchange differing from the authorize request; Slack app missing bot scopes/incorrect app type; Slack API incident returning malformed responses.

Related errors


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