{"record":{"id":"911214d087502af3","repo":"Budibase/budibase","slug":"failed-to-exchange-microsoft-oauth-code","errorCode":null,"errorMessage":"Failed to exchange Microsoft OAuth code","messagePattern":"Failed to exchange Microsoft OAuth code","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server/src/api/controllers/ai/sharepointAuth.ts","lineNumber":158,"sourceCode":"    },\n    body: new URLSearchParams({\n      client_id: clientId,\n      client_secret: clientSecret,\n      grant_type: \"authorization_code\",\n      code,\n      redirect_uri: callbackUrl,\n      scope: DEFAULT_SCOPE,\n    }),\n  })\n  const tokenPayload = await tokenResponse.json()\n  if (!tokenResponse.ok) {\n    console.error(\"Microsoft OAuth token exchange failed\", {\n      appId,\n      status: tokenResponse.status,\n      error: tokenPayload?.error,\n      hasDescription: !!tokenPayload?.error_description,\n    })\n    throw new Error(\"Failed to exchange Microsoft OAuth code\")\n  }\n\n  const refreshToken = tokenPayload?.refresh_token\n  const accessToken = tokenPayload?.access_token\n  if (!refreshToken) {\n    throw new Error(\"Microsoft OAuth response did not include a refresh token\")\n  }\n  if (!accessToken) {\n    throw new Error(\"Microsoft OAuth response did not include an access token\")\n  }\n\n  const expiresIn = Number(tokenPayload?.expires_in || 0)\n  const tokenType = tokenPayload?.token_type || \"Bearer\"\n  const bearerToken = `${tokenType} ${accessToken}`\n  let account = \"unknown\"\n\n  try {\n    const meResponse = await fetch(","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/api/controllers/ai/sharepointAuth.ts#L140-L176","documentation":"The token exchange POST to Microsoft's token endpoint returned a non-success status or an error payload; the controller logs status/error details and throws this Error. The code-for-token exchange did not produce usable credentials.","triggerScenarios":"Authorization code already used or expired (codes are single-use, short-lived); wrong redirect_uri sent in the token request; invalid client secret; tenant mismatch between authorize and token endpoints; network/proxy failure to login.microsoftonline.com.","commonSituations":"Replaying an auth code after a retry; redirect_uri differing between authorize and token calls (invalid_grant); expired or rotated MICROSOFT_CLIENT_SECRET (invalid_client); clock skew affecting code validity.","solutions":["Check server logs for the 'Microsoft OAuth token exchange failed' entry showing status and error (e.g. invalid_grant, invalid_client).","Ensure the redirect_uri, client_id, and tenant in the token request exactly match the authorize request.","Restart the OAuth flow to get a fresh authorization code; codes cannot be reused.","Verify MICROSOFT_CLIENT_SECRET is current and correctly loaded in the server environment."],"exampleFix":"// before\n// token request with redirect_uri that differs from the authorize call\nredirect_uri: 'http://localhost:10000/callback'\n// after\nredirect_uri: 'https://app.example.com/api/ai/sharepoint/callback' // identical to authorize request","handlingStrategy":"retry","validationCode":"// ensure a fresh, unused code: start a new flow if the callback has already been processed once","typeGuard":null,"tryCatchPattern":"try {\n  await completeSharePointAuth(ctx)\n} catch (e) {\n  if (e.message === 'Failed to exchange Microsoft OAuth code') {\n    // check logs for invalid_grant/invalid_client, then restart the OAuth flow with a new code\n  } else throw e\n}","preventionTips":["Use the identical redirect_uri in authorize and token requests.","Never reuse authorization codes; they are single-use and short-lived.","Rotate and verify MICROSOFT_CLIENT_SECRET; watch for invalid_client errors.","Monitor the token endpoint status in logs to distinguish config vs transient failures."],"tags":["oauth","microsoft","token-exchange","network","sharepoint"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}