{"record":{"id":"32b9fc15a04c8131","repo":"calcom/cal.diy","slug":"applecalendar-error-message","errorCode":null,"errorMessage":"${appleCalendar.error?.message}","messagePattern":"\\$\\{appleCalendar\\.error\\?\\.message\\}","errorType":"http","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"apps/api/v2/src/platform/calendars/services/apple-calendar.service.ts","lineNumber":54,"sourceCode":"    );\n\n    if (!appleCalendarCredentials) {\n      throw new BadRequestException(\"Credentials for apple calendar not found.\");\n    }\n\n    if (appleCalendarCredentials.invalid) {\n      throw new BadRequestException(\"Invalid apple calendar credentials.\");\n    }\n\n    const { connectedCalendars } = await this.calendarsService.getCalendars(userId);\n    const appleCalendar = connectedCalendars.find(\n      (cal: { integration: { type: string } }) => cal.integration.type === APPLE_CALENDAR_TYPE\n    );\n    if (!appleCalendar) {\n      throw new UnauthorizedException(\"Apple calendar not connected.\");\n    }\n    if (appleCalendar.error?.message) {\n      throw new UnauthorizedException(appleCalendar.error?.message);\n    }\n\n    return {\n      status: SUCCESS_STATUS,\n    };\n  }\n\n  async saveCalendarCredentials(userId: number, userEmail: string, username: string, password: string) {\n    if (!username || !password || username.length <= 1 || password.length <= 1) {\n      throw new BadRequestException(`Username or password cannot be empty`);\n    }\n\n    const existingAppleCalendarCredentials = await this.credentialRepository.getAllUserCredentialsByTypeAndId(\n      APPLE_CALENDAR_TYPE,\n      userId\n    );\n\n    let hasMatchingUsernameAndPassword = false;","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/calendars/services/apple-calendar.service.ts#L36-L72","documentation":"Thrown by AppleCalendarService.checkIfCalendarConnected (apple-calendar.service.ts:54) as UnauthorizedException (HTTP 401) when the connected Apple calendar object carries a truthy error.message produced by the upstream Cal.com platform-libraries integration. The raw upstream message is re-thrown verbatim, so the actual cause (DAV 401, unreachable host, expired token) is embedded in the response body.","triggerScenarios":"Apple's CalDAV server returns 401 because the app-specific password was revoked; network error reaching caldav.apple.com; token/session expired inside the integration; Apple rate-limited the request.","commonSituations":"Transient Apple server outage; revoked app-specific password; egress/firewall blocking the CalDAV port; intermittent auth failures during high load.","solutions":["Read the dynamic message in the 401 response body — it contains the integration's verbatim error text (e.g., 'Invalid credentials').","If the message is auth-related, re-save credentials (see error 361 fix).","If the message indicates a network/timeout condition, retry with backoff."],"exampleFix":"// before: surfacing only a generic error\ntry { await api.get('/v2/calendars/apple_calendar/check'); }\ncatch (e) { console.error('check failed'); }\n\n// after: read the upstream message embedded by the service\ntry { await api.get('/v2/calendars/apple_calendar/check'); }\ncatch (e) {\n  const upstreamMsg = e.response?.data?.message; // e.g. 'Invalid credentials'\n  if (/credential|auth|401/i.test(upstreamMsg)) await reSaveAppleCredentials();\n  else await retryWithBackoff(check);\n}","handlingStrategy":"try-catch","validationCode":"// Inspect the upstream error surfaced via the calendars list first\nconst { data } = await api.get('/v2/calendars');\nconst apple = data.connectedCalendars.find(c => c.integration?.type === 'apple_calendar');\nif (apple?.error?.message) {\n  // pre-handle: do not call check; the upstream error is already known\n  throw new UpstreamError(apple.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/apple_calendar/check');\n} catch (e) {\n  const msg = e.response?.data?.message ?? '';\n  if (e.response?.status === 401) {\n    if (/auth|credential|password|401/i.test(msg)) await reSaveAppleCredentials();\n    else await retryWithBackoff(() => api.get('/v2/calendars/apple_calendar/check'));\n    return;\n  }\n  throw e;\n}","preventionTips":["Surface the upstream error.message to operators (log it) — it is the real diagnosis.","Retry transient/network upstream errors with exponential backoff.","Distinguish auth-related upstream messages from transient ones before re-prompting the user."],"tags":["apple-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"}