{"record":{"id":"b7ef55d69e41b1a3","repo":"calcom/cal.diy","slug":"invalid-access-token-b7ef55","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/gcal.service.ts","lineNumber":151,"sourceCode":"    isDryRun?: boolean\n  ) {\n    // User chose not to authorize your app or didn't authorize your app\n    // redirect directly without oauth code\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 oAuth2Client = await this.getOAuthClient(this.redirectUri);\n    const token = await oAuth2Client.getToken(parsedCode);\n    // Google oAuth Credentials are stored in token.tokens\n    const key = token.tokens;\n\n    oAuth2Client.setCredentials(key);\n\n    const calendar = new calendar_v3.Calendar({\n      auth: oAuth2Client,\n    });\n\n    const cals = await calendar.calendarList.list({ fields: \"items(id,summary,primary,accessRole)\" });\n\n    const primaryCal = cals.data.items?.find((cal) => cal.primary);\n\n    if (primaryCal?.id) {","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/calendars/services/gcal.service.ts#L133-L169","documentation":"Thrown by GoogleCalendarService.saveCalendarCredentialsAndRedirect after the OAuth redirect callback returns from Google. The handler calls tokensService.getAccessTokenOwnerId(accessToken) to resolve the Cal API user that owns the supplied platform access token; if no owner row maps to the token, the request is treated as unauthenticated and HTTP 401 UnauthorizedException('Invalid Access token.') is raised. This protects the OAuth-code exchange step (the subsequent oAuth2Client.getToken call and credential persistence) so a credential is only ever stored against a verified user.","triggerScenarios":"Calling GET /v2/calendars/google/connect/callback (or saveCalendarCredentialsAndRedirect) with an accessToken that is expired, revoked, malformed, or belongs to no user; passing the same token after it was rotated; the Authorization header / query token is missing so accessToken resolves to undefined.","commonSituations":"Token TTL elapsed between the start of the OAuth flow and the redirect callback; the access token was issued by a different Cal API deployment/environment (env mismatch); the client sent the refresh token or an opaque ID instead of the platform access token; clock skew on the server makes a valid token appear expired.","solutions":["Re-authenticate via the platform OAuth flow to mint a fresh access token, then retry the calendar callback.","Verify the token being passed is the Cal.com platform access token (not a Google OAuth code or Cal refresh token) and that it is sent in the header/param the handler expects.","Confirm the API host issuing the token and the host serving the callback are the same deployment and share the same token-signing secret/database.","Inspect the tokensService.getAccessTokenOwnerId implementation to confirm the token is being decoded against the correct secret and that the owner row still exists."],"exampleFix":"// before\nconst res = await fetch(`/v2/calendars/google/connect/callback?code=${code}`, { headers: {} });\n\n// after\nconst res = await fetch(`/v2/calendars/google/connect/callback?code=${code}`, {\n  headers: { Authorization: `Bearer ${calAccessToken}` },\n});","handlingStrategy":"validation","validationCode":"async function ensureTokenOwner(tokensService, accessToken) {\n  const ownerId = await tokensService.getAccessTokenOwnerId(accessToken);\n  if (!ownerId) {\n    return { ok: false, reason: 'Invalid Access token.' };\n  }\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 googleCalendarService.saveCalendarCredentialsAndRedirect(code, accessToken, origin);\n} catch (e) {\n  if (e instanceof UnauthorizedException && e.message === 'Invalid Access token.') {\n    // re-authenticate the user, then retry once\n  } else throw e;\n}","preventionTips":["Refresh the Cal access token before the OAuth redirect if the flow may outlast its TTL.","Pass the token in the exact location the handler reads (Authorization header).","Keep the deployment that issued the token and the deployment serving the callback identical."],"tags":["authentication","oauth","google-calendar","api-v2"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}