{"record":{"id":"75b9c7a0e644ce40","repo":"calcom/cal.diy","slug":"google-calendar-not-connected","errorCode":null,"errorMessage":"Google Calendar not connected.","messagePattern":"Google Calendar not connected\\.","errorType":"http","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"apps/api/v2/src/platform/calendars/services/gcal.service.ts","lineNumber":119,"sourceCode":"    const gcalCredentials = await this.credentialRepository.findCredentialByTypeAndUserId(\n      \"google_calendar\",\n      userId\n    );\n\n    if (!gcalCredentials) {\n      throw new BadRequestException(\"Credentials for google_calendar not found.\");\n    }\n\n    if (gcalCredentials.invalid) {\n      throw new BadRequestException(\"Invalid google OAuth credentials.\");\n    }\n\n    const { connectedCalendars } = await this.calendarsService.getCalendars(userId);\n    const googleCalendar = connectedCalendars.find(\n      (cal: { integration: { type: string } }) => cal.integration.type === GOOGLE_CALENDAR_TYPE\n    );\n    if (!googleCalendar) {\n      throw new UnauthorizedException(\"Google Calendar not connected.\");\n    }\n    if (googleCalendar.error?.message) {\n      throw new UnauthorizedException(googleCalendar.error?.message);\n    }\n\n    return { status: SUCCESS_STATUS };\n  }\n\n  async saveCalendarCredentialsAndRedirect(\n    code: string,\n    accessToken: string,\n    origin: string,\n    redir?: string,\n    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\") {","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/calendars/services/gcal.service.ts#L101-L137","documentation":"Thrown by GoogleCalendarService.checkIfCalendarConnected (gcal.service.ts:119) as UnauthorizedException (HTTP 401) when the credential exists and is valid but calendarsService.getCalendars(userId) returns no connected calendar of type google_calendar. The credential is stored but no Google calendar is surfaced/linked. Mirror of error 362.","triggerScenarios":"OAuth completed but the primary calendar was never linked (saveCalendarCredentialsAndRedirect didn't reach createAndLinkCalendarEntry); cache stale; selectedCalendars row missing for the primary calendar id.","commonSituations":"Save flow returned early (no primaryCal.id, or alreadyExistingSelectedCalendar branch); cache not invalidated after connect; race between save and check.","solutions":["Invalidate the connected-calendars cache for the user and retry check.","Re-run the save callback so createAndLinkCalendarEntry links the primary calendar.","Confirm a SelectedCalendars row exists for the google primary calendar externalId + credentialId."],"exampleFix":"// before: check right after save returns 401 (stale cache / not linked)\nawait api.get('/v2/calendars/google_calendar/check'); // 401\n\n// after: clear cache server-side then re-check\ncalendarsCacheService.deleteConnectedAndDestinationCalendarsCache(userId);\nawait api.get('/v2/calendars/google_calendar/check'); // 200 once linked","handlingStrategy":"validation","validationCode":"// Confirm the google calendar is linked (after cache refresh) before check\nconst { data } = await api.get('/v2/calendars', { params: { ensureDefaultSelectedCalendars: true } });\nconst linked = data.connectedCalendars.some(c => c.integration?.type === 'google_calendar');\nif (!linked) throw new Error('Google calendar did not link — re-run save');","typeGuard":"function hasConnectedGoogle(list) {\n  return Array.isArray(list) && list.some(c => c?.integration?.type === 'google_calendar');\n}","tryCatchPattern":"try {\n  await api.get('/v2/calendars/google_calendar/check');\n} catch (e) {\n  if (e.response?.status === 401 && /not connected/i.test(e.response?.data?.message)) {\n    // re-run save callback to link the primary calendar, then retry once\n    await reRunGoogleSaveCallback();\n    return api.get('/v2/calendars/google_calendar/check');\n  }\n  throw e;\n}","preventionTips":["Invalidate CalendarsCacheService after the Google save callback links the primary calendar.","Confirm createAndLinkCalendarEntry actually ran (primaryCal.id present) before reporting success.","Do not poll check immediately after save — allow cache invalidation to propagate."],"tags":["google-calendar","connection","cache","unauthorized","http-401"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}