{"record":{"id":"8402c03d416d6da5","repo":"Budibase/budibase","slug":"slack-oauth-callback-is-missing-the-authorization","errorCode":null,"errorMessage":"Slack OAuth callback is missing the authorization code","messagePattern":"Slack OAuth callback is missing the authorization code","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server/src/api/controllers/ai/agents.ts","lineNumber":592,"sourceCode":"  }\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,\n      clientId,\n      clientSecret,\n      redirectUri,\n    })\n    const botToken = token.access_token?.trim()","sourceCodeStart":574,"sourceCodeEnd":610,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/api/controllers/ai/agents.ts#L574-L610","documentation":"On a successful (non-error) callback, Slack returns ?code=... which is exchanged for a bot token. If the code parameter is missing or empty despite no error being present, the handler throws this Error because the token exchange cannot proceed.","triggerScenarios":"Callback arrives with neither error nor code — truncated redirect URL, duplicate query handling, Slack returning an unexpected redirect shape, or the user re-submitting a callback whose code was already consumed/stripped.","commonSituations":"Proxies rewriting the redirect and dropping query params; users re-opening a callback URL from history after the code was used; misconfigured redirect URL in the Slack app settings.","solutions":["Restart the OAuth flow from initiation — authorization codes are single-use and short-lived","Verify the Slack app's redirect URL matches exactly and does not get rewritten by proxies","Ensure the authorize URL requests the expected response_type=code flow","Log the full callback query server-side to see what Slack actually returned"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const url = new URL(callbackUrl)\nif (!url.searchParams.get(\"error\") && !url.searchParams.get(\"code\")) {\n  throw new Error(\"Callback has neither code nor error — restart the OAuth flow\")\n}","typeGuard":"function hasOAuthCode(q: Record<string, unknown>): q is Record<string, string> & { code: string } {\n  return typeof q.code === \"string\" && q.code.trim() !== \"\"\n}","tryCatchPattern":"try {\n  await completeSlackOAuth(ctx)\n} catch (err) {\n  if (err.message.includes(\"missing the authorization code\")) {\n    // restart flow; codes are single-use so retrying the callback never works\n  } else { throw err }\n}","preventionTips":["Never retry a consumed callback — always re-initiate for a fresh code","Verify redirect URL configuration so Slack returns the full query string","Avoid URL rewriting middleware on the callback route","Log callback query keys (not values) to diagnose stripped params"],"tags":["slack","oauth","authorization-code","callback"],"backgroundTag":"oauth-code-missing","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}