{"record":{"id":"889094bf5e0e5891","repo":"calcom/cal.diy","slug":"google-calendar-is-not-connected-for-this-user","errorCode":null,"errorMessage":"Google Calendar is not connected for this user","messagePattern":"Google Calendar is not connected for this user","errorType":"http","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"apps/api/v2/src/modules/cal-unified-calendars/services/google-calendar.service.ts","lineNumber":177,"sourceCode":"\n      await authClient.authorize();\n      return new calendar_v3.Calendar({ auth: authClient });\n    } 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) {","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/cal-unified-calendars/services/google-calendar.service.ts#L159-L195","documentation":"Thrown by getCalendarClientForUser when findCredentialWithDelegationByTypeAndUserId returns null — no AppCredential row of type google_calendar exists for the given userId. Returns HTTP 401 via UnauthorizedException. This is the user-scoped entry point used by listEventsForUser/createEventForUser/deleteEventForUser.","triggerScenarios":"Calling any user-scoped unified-calendar endpoint (POST /v2/calendars/{calendar}/events, GET .../events, DELETE) for a user who never connected Google Calendar, or whose only google_calendar credential belongs to a different userId (team/org credentials are not returned by this user-scoped query).","commonSituations":"User signed up but skipped calendar onboarding; the google_calendar credential was deleted via the disconnect flow; testing with a userId from a different environment; user connected only Office 365 or Apple.","solutions":["Have the user complete the Google Calendar OAuth connect flow to create a google_calendar credential row.","Verify the userId passed matches the credential owner (SELECT \"userId\", type FROM \"AppCredential\" WHERE type='google_calendar' AND \"userId\"=?).","If the user should use a team/org-scoped calendar, call the connection-scoped endpoints with the correct credentialId instead.","Surface a clear UI prompt to connect Google Calendar before allowing event operations."],"exampleFix":"// before\nconst events = await googleCalendarService.listEventsForUser(userId, calId, t0, t1);\n\n// after: validate connection state first\nconst cred = await credentialsRepository.findCredentialWithDelegationByTypeAndUserId(GOOGLE_CALENDAR_TYPE, userId);\nif (!cred) {\n  return { status: 'requires_connection', url: '/apps/google-calendar' };\n}","handlingStrategy":"validation","validationCode":"async function ensureGoogleCalendarConnected(userId: number): Promise<void> {\n  const cred = await credentialsRepository.findCredentialWithDelegationByTypeAndUserId(GOOGLE_CALENDAR_TYPE, userId);\n  if (!cred) {\n    throw new Error(`User ${userId} has no google_calendar credential. Direct them to connect.`);\n  }\n}\n\nawait ensureGoogleCalendarConnected(userId);","typeGuard":"interface HasGoogleCalendar {\n  type: string;\n  userId: number;\n}\n\nfunction isGoogleCalendarCredential<T extends { type: string }>(c: T | null): c is T & { type: 'google_calendar' } {\n  return c !== null && c.type === 'google_calendar';\n}","tryCatchPattern":"try {\n  await googleCalendarService.listEventsForUser(userId, calId, t0, t1);\n} catch (e) {\n  if (e instanceof UnauthorizedException && /not connected/.test(e.message)) {\n    return res.status(422).json({ code: 'calendar_not_connected', connectUrl: '/apps/google-calendar' });\n  }\n  throw e;\n}","preventionTips":["Surface a 'Connect Google Calendar' step in onboarding before exposing event endpoints.","Cache the connection state in the session so the UI knows it before each call.","Return a structured error code (calendar_not_connected) instead of relying on message matching."],"tags":["google-calendar","oauth","credentials","nestjs","onboarding"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}