{"record":{"id":"6e0de3ad81e9bad4","repo":"calcom/cal.diy","slug":"googlecalendar-error-message","errorCode":null,"errorMessage":"${googleCalendar.error?.message}","messagePattern":"\\$\\{googleCalendar\\.error\\?\\.message\\}","errorType":"http","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"apps/api/v2/src/platform/calendars/services/gcal.service.ts","lineNumber":122,"sourceCode":"    );\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\") {\n      return { url: redir || origin };\n    }\n","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/calendars/services/gcal.service.ts#L104-L140","documentation":"Thrown by GoogleCalendarService.checkIfCalendarConnected (gcal.service.ts:122) as UnauthorizedException (HTTP 401) when the connected Google calendar object carries a truthy error.message produced by the upstream integration. The raw upstream message is re-thrown verbatim, so the actual cause (expired access token, quota, Google API error) is embedded in the response body. Mirror of error 363.","triggerScenarios":"Google Calendar API returned 401 because the access token expired and refresh failed; daily quota exceeded; transient Google API outage; revoked consent surfaced as an integration error.","commonSituations":"Expired access token; user revoked access in Google account; Google API rate limiting; intermittent failures under load.","solutions":["Read the dynamic message in the 401 response body — it carries the integration's verbatim error text.","If the message is auth/token-related, re-run the OAuth connect→save flow (see error 377).","If the message indicates quota/transient failure, retry with backoff."],"exampleFix":"// before: generic error handling\ntry { await api.get('/v2/calendars/google_calendar/check'); }\ncatch (e) { throw e; }\n\n// after: branch on the upstream message\ntry { await api.get('/v2/calendars/google_calendar/check'); }\ncatch (e) {\n  const msg = e.response?.data?.message ?? '';\n  if (/token|credential|401|unauthorized/i.test(msg)) await reAuthGoogle();\n  else if (/quota|rate|retry/i.test(msg)) await retryWithBackoff(check);\n  else throw e;\n}","handlingStrategy":"try-catch","validationCode":"// Inspect the upstream error from the list before calling check\nconst { data } = await api.get('/v2/calendars');\nconst gcal = data.connectedCalendars.find(c => c.integration?.type === 'google_calendar');\nif (gcal?.error?.message) {\n  throw new UpstreamError(gcal.error.message);\n}","typeGuard":"function hasUpstreamError(conn) {\n  return typeof conn?.error?.message === 'string' && conn.error.message.length > 0;\n}","tryCatchPattern":"try {\n  await api.get('/v2/calendars/google_calendar/check');\n} catch (e) {\n  const msg = e.response?.data?.message ?? '';\n  if (e.response?.status === 401) {\n    if (/token|credential|unauthorized|401/i.test(msg)) await reAuthGoogle();\n    else await retryWithBackoff(() => api.get('/v2/calendars/google_calendar/check'));\n    return;\n  }\n  throw e;\n}","preventionTips":["Read the upstream message embedded in the 401 body — it is the real diagnosis.","Retry transient/quota upstream errors with backoff; re-auth on token-related messages.","Log the upstream error server-side for monitoring since the HTTP layer just forwards it."],"tags":["google-calendar","upstream","dynamic-message","auth","http-401"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}