{"record":{"id":"6e2048281e447b49","repo":"calcom/cal.diy","slug":"calendar-credentials-are-invalid-please-reconnect","errorCode":null,"errorMessage":"Calendar credentials are invalid. Please reconnect.","messagePattern":"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":204,"sourceCode":"    );\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) {\n      throw new BadRequestException(\n        \"Event operations for this connection are currently only available for Google Calendar\"\n      );\n    }\n    if (credential.invalid) {\n      throw new UnauthorizedException(\"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  // ─── Shared private helpers (DRY calendar CRUD) ──────────────────────\n\n  private async listEventsWithClient(\n    calendar: calendar_v3.Calendar,\n    calendarId: string,\n    timeMin: string,\n    timeMax: string\n  ): Promise<GoogleCalendarEventResponse[]> {\n    const effectiveCalendarId = calendarId || \"primary\";\n    try {","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/cal-unified-calendars/services/google-calendar.service.ts#L186-L222","documentation":"Connection-scoped counterpart of error 82. Thrown by getCalendarClientByCredentialId when the credential exists, is google_calendar, but invalid is true. Returns HTTP 401. Means the credential was marked unusable by a prior failed sync.","triggerScenarios":"Calling /v2/calendars/connections/{connectionId}/events with a connectionId whose underlying Google credential has invalid=true (refresh token revoked or refresh failed during a prior sync).","commonSituations":"Same root causes as 82 but reached via the connection-scoped API surface; the user reconnected and got a new credential row but the client still references the old connectionId.","solutions":["Reconnect Google Calendar to obtain a fresh credential, then use the new connectionId.","Pre-check the connection's status field from the connections list and block the action when invalid.","If reconnecting yields a new credentialId, update any stored references in client state.","Inspect sync logs for why invalid was set on this credential."],"exampleFix":"// before\nconst cal = await googleCalendarService.getCalendarClientByCredentialId(userId, credentialId);\n\n// after\nconst cred = await credentialsRepository.findCredentialByIdAndUserId(credentialId, userId);\nif (cred?.invalid) {\n  return { status: 'error', code: 'credential_invalid', action: 'reconnect_google_calendar' };\n}","handlingStrategy":"validation","validationCode":"async function connectionIsUsable(connectionId: number, userId: number): Promise<boolean> {\n  const cred = await credentialsRepository.findCredentialByIdAndUserId(connectionId, userId);\n  return Boolean(cred && !cred.invalid);\n}\n\nif (!(await connectionIsUsable(credentialId, userId))) {\n  return { code: 'reconnect_google_calendar', connectionId };\n}","typeGuard":"function isActiveCredential<T extends { invalid?: boolean }>(c: T | null): c is T {\n  return c !== null && c.invalid !== true;\n}","tryCatchPattern":"try {\n  await googleCalendarService.listEventsForUserByConnectionId(userId, credentialId, calId, t0, t1);\n} catch (e) {\n  if (e instanceof UnauthorizedException && /invalid/i.test(e.message)) {\n    return res.status(422).json({ code: 'credential_invalid', connectionId });\n  }\n  throw e;\n}","preventionTips":["After a reconnect, immediately discard any cached connectionId and re-fetch.","Expose the invalid flag on connection DTOs so the UI can show a 'reconnect' state proactively.","Run periodic probes on stored credentials to catch invalid tokens before the user does."],"tags":["google-calendar","oauth","token-revoked","credentials","nestjs","connection"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}