{"record":{"id":"e95d020b3c014d2e","repo":"calcom/cal.diy","slug":"google-calendar-credentials-are-invalid-please-re","errorCode":null,"errorMessage":"Google Calendar credentials are invalid. Please reconnect.","messagePattern":"Google Calendar credentials are invalid\\. Please reconnect\\.","errorType":"http","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"apps/api/v2/src/modules/cal-unified-calendars/services/google-calendar.service.ts","lineNumber":180,"sourceCode":"    } catch (error) {\n      return null;\n    }\n  }\n\n  /**\n   * Gets an authorized Google Calendar instance for the given user (for user-scoped list/create/delete).\n   * Tries delegated auth first (if available), then falls back to direct OAuth.\n   */\n  async getCalendarClientForUser(userId: number): Promise<calendar_v3.Calendar> {\n    const credential = await this.credentialsRepository.findCredentialWithDelegationByTypeAndUserId(\n      GOOGLE_CALENDAR_TYPE,\n      userId\n    );\n    if (!credential) {\n      throw new UnauthorizedException(\"Google Calendar is not connected for this user\");\n    }\n    if (credential.invalid) {\n      throw new UnauthorizedException(\"Google Calendar credentials are invalid. Please reconnect.\");\n    }\n    return this.getAuthorizedCalendarInstance(\n      credential.user?.email ?? undefined,\n      credential.key,\n      credential.delegationCredentialId ? { id: credential.delegationCredentialId } : null\n    );\n  }\n\n  /**\n   * Gets an authorized Google Calendar instance for a specific credential (connection).\n   * Tries delegated auth first (if available), then falls back to direct OAuth.\n   */\n  async getCalendarClientByCredentialId(userId: number, credentialId: number): Promise<calendar_v3.Calendar> {\n    const credential = await this.credentialsRepository.findCredentialByIdAndUserId(credentialId, userId);\n    if (!credential) {\n      throw new NotFoundException(\"Calendar connection not found\");\n    }\n    if (credential.type !== GOOGLE_CALENDAR_TYPE) {","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/cal-unified-calendars/services/google-calendar.service.ts#L162-L198","documentation":"Thrown by getCalendarClientForUser when a google_calendar credential exists but its invalid boolean flag is true. The invalid flag is set by Cal.com's sync engine when Google rejects the stored refresh token (revoked, expired, app access revoked). Returns HTTP 401. Distinct from error 81: the row exists but is marked unusable.","triggerScenarios":"The user revoked Calendar access from their Google account security page; the refresh token expired after 6 months of inactivity; an admin revoked the OAuth client; a failed sync set invalid=true and the user then calls any user-scoped calendar operation.","commonSituations":"Long-inactive user returning; Google Workspace admin disabled the app; password change on the Google account invalidating tokens; stale test credentials.","solutions":["Direct the user to reconnect Google Calendar through the OAuth flow, which upserts a fresh credential row with invalid=false.","Before the call, check credential.invalid and short-circuit with a 'reconnect' instruction.","Audit recent sync logs for the credential to see why invalid was set (token refresh failure).","If using delegation, confirm the delegated service account still has Calendar API access in Workspace admin."],"exampleFix":"// before\nconst cal = await googleCalendarService.getCalendarClientForUser(userId);\n\n// after\nconst cred = await credentialsRepository.findCredentialWithDelegationByTypeAndUserId(GOOGLE_CALENDAR_TYPE, userId);\nif (cred?.invalid) {\n  throw new UnauthorizedException('Google Calendar token revoked — please reconnect at /apps/google-calendar.');\n}","handlingStrategy":"validation","validationCode":"async function googleCalendarIsUsable(userId: number): Promise<boolean> {\n  const cred = await credentialsRepository.findCredentialWithDelegationByTypeAndUserId(GOOGLE_CALENDAR_TYPE, userId);\n  return Boolean(cred && !cred.invalid);\n}\n\nif (!(await googleCalendarIsUsable(userId))) {\n  return { code: 'reconnect_google_calendar' };\n}","typeGuard":"function isUsableCredential<T extends { invalid?: boolean }>(c: T | null): c is T {\n  return c !== null && c.invalid !== true;\n}","tryCatchPattern":"try {\n  const cal = await googleCalendarService.getCalendarClientForUser(userId);\n} catch (e) {\n  if (e instanceof UnauthorizedException && /invalid/i.test(e.message)) {\n    // token revoked — push reconnect notification\n    await notifyUser(userId, 'google_calendar_revoked');\n    throw new ApiError('reconnect_required', 422);\n  }\n  throw e;\n}","preventionTips":["Monitor the sync engine's invalid-flag setter and proactively notify affected users.","Expose credential.invalid on the connections list DTO so clients can show a 'reconnect' badge.","Run a daily refresh probe for low-activity users to detect revoked tokens early."],"tags":["google-calendar","oauth","token-revoked","credentials","nestjs","auth"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}