{"record":{"id":"3b55a2f8143388b1","repo":"Budibase/budibase","slug":"microsoft-oauth-response-did-not-include-an-access","errorCode":null,"errorMessage":"Microsoft OAuth response did not include an access token","messagePattern":"Microsoft OAuth response did not include an access token","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server/src/api/controllers/ai/sharepointAuth.ts","lineNumber":167,"sourceCode":"  })\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(\n      `${MICROSOFT_GRAPH_BASE}/me?$select=displayName,mail,userPrincipalName`,\n      {\n        headers: {\n          Authorization: bearerToken,\n        },\n      }\n    )\n    if (meResponse.ok) {\n      const mePayload = await meResponse.json()","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/api/controllers/ai/sharepointAuth.ts#L149-L185","documentation":"completeSharePointAuth requires an access_token from Microsoft's token exchange to authenticate subsequent SharePoint API calls. This error means the code exchange succeeded (HTTP 200) but the response body contained no access_token, so the OAuth flow cannot proceed.","triggerScenarios":"Microsoft's token endpoint returns an unexpected payload (e.g. an error-shaped JSON or empty body with 200) when completing the SharePoint OAuth code exchange.","commonSituations":"Misconfigured redirect URI causing an error response being parsed as a payload; wrong tenant/endpoint (v1 vs v2 token endpoint); network proxy returning an HTML error page with 200.","solutions":["Log the HTTP status and tokenPayload shape to see what Microsoft actually returned","Verify the token request uses the correct endpoint (https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token), client_id, client_secret, and redirect_uri matching the app registration","Retry the authorization flow; if the code was already redeemed, request a fresh code"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const res = await fetch(tokenEndpoint, { method: 'POST', body })\nif (!res.ok) throw new Error(`Token endpoint returned ${res.status}`)\nconst payload = await res.json()\nif (typeof payload.access_token !== 'string') throw new Error('No access_token in response: ' + JSON.stringify(Object.keys(payload)))","typeGuard":"function hasAccessToken(p: unknown): p is { access_token: string } {\n  return typeof p === 'object' && p !== null && 'access_token' in p && typeof (p as { access_token: unknown }).access_token === 'string'\n}","tryCatchPattern":"try {\n  await completeSharePointAuth(params)\n} catch (e) {\n  if (e.message.includes('access token')) {\n    // inspect raw token response and restart authorization flow with a fresh code\n  }\n}","preventionTips":["Validate the raw token endpoint response status and body shape before parsing","Keep redirect_uri, client_id, and endpoint version identical between authorize and token requests","Never reuse authorization codes; each code can be redeemed only once"],"tags":["oauth","microsoft","sharepoint","missing-token"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}