{"record":{"id":"514ed7a46b74530c","repo":"Budibase/budibase","slug":"slack-oauth-authorization-failed","errorCode":null,"errorMessage":"Slack OAuth authorization failed","messagePattern":"Slack OAuth authorization failed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/server/src/api/controllers/ai/agents.ts","lineNumber":587,"sourceCode":"\nexport async function completeSlackOAuth(ctx: UserCtx<void, void>) {\n  const state = String(ctx.query.state || \"\").trim()\n  if (!state) {\n    throw new Error(\"Slack OAuth callback is missing state\")\n  }\n\n  const cacheKey = getSlackOAuthStateCacheKey(state)\n  const statePayload = (await cache.get(cacheKey, {\n    useTenancy: false,\n  })) as SlackOAuthState | undefined\n  await cache.destroy(cacheKey, { useTenancy: false })\n  if (!statePayload?.agentId || !statePayload.workspaceId) {\n    throw new Error(\"Slack OAuth state is invalid or expired\")\n  }\n\n  const oauthError = String(ctx.query.error || \"\").trim()\n  if (oauthError) {\n    throw new Error(\"Slack OAuth authorization failed\")\n  }\n\n  const code = String(ctx.query.code || \"\").trim()\n  if (!code) {\n    throw new Error(\"Slack OAuth callback is missing the authorization code\")\n  }\n\n  await context.doInWorkspaceContext(statePayload.workspaceId, async () => {\n    const agent = await sdk.ai.agents.getOrThrow(statePayload.agentId)\n    const clientId = agent.slackIntegration?.clientId?.trim()\n    const clientSecret = agent.slackIntegration?.clientSecret?.trim()\n    if (!clientId || !clientSecret) {\n      throw new Error(\"Slack OAuth client credentials are not configured\")\n    }\n\n    const redirectUri = await getSlackOAuthRedirectUrl()\n    const token = await sdk.ai.deployments.slack.exchangeSlackOAuthCode({\n      code,","sourceCodeStart":569,"sourceCodeEnd":605,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/api/controllers/ai/agents.ts#L569-L605","documentation":"After validating state, completeSlackOAuth checks the callback query for an error parameter, which Slack sets when the user denies the app or authorization fails. If present, it throws this Error to surface the upstream authorization failure.","triggerScenarios":"Slack redirects back to the callback with ?error=... (or error=subdomain etc.) — the end user clicked 'Cancel' on the Slack consent screen, the app was rejected, or Slack refused the authorize request.","commonSituations":"User cancels the Slack permission screen; workspace requires admin app approval and it was denied; app not approved/distributed for that workspace; Slack-side policy blocking installation.","solutions":["Retry the install and click 'Allow' on the Slack consent screen","If Slack demands admin approval, have a workspace admin approve the app installation then retry","Verify the app's scopes and distribution settings in the Slack app configuration","Surface the error query value to the end user so they know why authorization failed"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const oauthError = new URL(callbackUrl).searchParams.get(\"error\")\nif (oauthError) {\n  // show user-facing message before invoking the callback handler\n  return { ok: false, reason: oauthError }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await completeSlackOAuth(ctx)\n} catch (err) {\n  if (err.message === \"Slack OAuth authorization failed\") {\n    // user denied the app or Slack returned error=; show a friendly restart-install message\n  } else { throw err }\n}","preventionTips":["Tell users ahead of time that cancelling the Slack consent screen aborts the install","Handle the error query param in the client callback UI with a clear message","Check app distribution/approval settings so installs are not silently blocked","Read ctx.query.error to display Slack's specific denial reason"],"tags":["slack","oauth","user-cancelled","authorization"],"backgroundTag":"oauth-authorization-denied","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}