{"record":{"id":"767277441f5cadd9","repo":"calcom/cal.diy","slug":"invalid-access-token-767277","errorCode":null,"errorMessage":"Invalid Access token.","messagePattern":"Invalid Access token\\.","errorType":"http","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"apps/api/v2/src/platform/calendars/services/outlook.service.ts","lineNumber":184,"sourceCode":"    redir?: string,\n    isDryRun?: boolean\n  ) {\n    // if code is not defined, user denied to authorize office 365 app, just redirect straight away\n    if (!code || code === \"undefined\") {\n      return { url: redir || origin };\n    }\n\n    // if isDryRun is true we know its a dry run so we just redirect straight away\n    if (isDryRun) {\n      return { url: redir || origin };\n    }\n\n    const parsedCode = z.string().parse(code);\n\n    const ownerId = await this.tokensService.getAccessTokenOwnerId(accessToken);\n\n    if (!ownerId) {\n      throw new UnauthorizedException(\"Invalid Access token.\");\n    }\n\n    const office365OAuthCredentials = await this.getOAuthCredentials(parsedCode);\n\n    const defaultCalendar = await this.getDefaultCalendar(office365OAuthCredentials.access_token);\n\n    if (defaultCalendar?.id) {\n      const alreadyExistingSelectedCalendar = await this.selectedCalendarsRepository.getUserSelectedCalendar(\n        ownerId,\n        OFFICE_365_CALENDAR_TYPE,\n        defaultCalendar.id\n      );\n\n      if (alreadyExistingSelectedCalendar) {\n        const isCredentialValid = await this.calendarsService.checkCalendarCredentialValidity(\n          ownerId,\n          alreadyExistingSelectedCalendar.credentialId ?? 0,\n          OFFICE_365_CALENDAR_TYPE","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/calendars/services/outlook.service.ts#L166-L202","documentation":"Thrown by OutlookService.saveCalendarCredentialsAndRedirect, the OAuth-callback handler for Office 365. After Microsoft redirects back with an OAuth code, the handler resolves the calling user via tokensService.getAccessTokenOwnerId(accessToken); if no user maps to the supplied Cal platform access token, UnauthorizedException('Invalid Access token.') is raised before the code is exchanged for Graph credentials (getOAuthCredentials). This is the Outlook twin of error 380 in the Google service.","triggerScenarios":"The /v2/calendars/office365/connect/callback (or equivalent) is called with an accessToken that is expired, revoked, belongs to another environment, or was never sent; the token is sent in the wrong location (query vs Authorization header); the user took long enough on Microsoft's consent screen that the Cal access token expired before the callback.","commonSituations":"Long Microsoft consent flow outlasting the Cal access-token TTL; token issued by a different Cal deployment (env mismatch); client passed the OAuth code as the access token or vice-versa; clock skew on the API host.","solutions":["Re-authenticate with the Cal platform OAuth flow to obtain a fresh access token, then retry the Office 365 callback.","Confirm the token being passed is the Cal platform access token (not the Microsoft OAuth code) and is delivered where the handler reads it.","Ensure the deployment issuing the token and the deployment serving the callback are the same and share the signing secret/DB."],"exampleFix":"// before\nwindow.location.href = `${apiBase}/v2/calendars/office365/connect/callback?code=${msCode}`;\n\n// after\n// refresh calAccessToken first if it may have expired during consent\nwindow.location.href = `${apiBase}/v2/calendars/office365/connect/callback?code=${msCode}`;","handlingStrategy":"validation","validationCode":"async function ensureTokenOwner(tokensService, accessToken) {\n  const ownerId = await tokensService.getAccessTokenOwnerId(accessToken);\n  if (!ownerId) return { ok: false, reason: 'Invalid Access token.' };\n  return { ok: true, ownerId };\n}","typeGuard":"function isNonEmptyToken(t: unknown): t is string {\n  return typeof t === 'string' && t.trim().length > 0 && t !== 'undefined';\n}","tryCatchPattern":"try {\n  await outlookService.saveCalendarCredentialsAndRedirect(code, accessToken, origin);\n} catch (e) {\n  if (e instanceof UnauthorizedException && e.message === 'Invalid Access token.') {\n    // re-authenticate, then retry once\n  } else throw e;\n}","preventionTips":["Refresh the Cal access token before the long Microsoft consent flow.","Pass the Cal platform token (not the MS OAuth code) where the handler reads it.","Keep issuing and callback deployments identical."],"tags":["authentication","oauth","outlook","office365","api-v2"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}