{"record":{"id":"4dd6209c7ae9eb9e","repo":"calcom/cal.diy","slug":"icscalendar-error-message","errorCode":null,"errorMessage":"${icsCalendar.error?.message}","messagePattern":"\\$\\{icsCalendar\\.error\\?\\.message\\}","errorType":"http","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"apps/api/v2/src/platform/calendars/services/ics-feed.service.ts","lineNumber":109,"sourceCode":"\n    if (!icsFeedCredentials) {\n      throw new BadRequestException(\"Credentials for Ics Feed calendar not found.\");\n    }\n\n    if (icsFeedCredentials.invalid) {\n      throw new BadRequestException(\"Invalid Ics Feed credentials.\");\n    }\n\n    const { connectedCalendars } = await this.calendarsService.getCalendars(userId);\n    const icsCalendar = connectedCalendars.find(\n      (cal: { integration: { type: string } }) => cal.integration.type === ICS_CALENDAR_TYPE\n    );\n\n    if (!icsCalendar) {\n      throw new UnauthorizedException(\"Ics Feed not connected.\");\n    }\n    if (icsCalendar.error?.message) {\n      throw new UnauthorizedException(icsCalendar.error?.message);\n    }\n\n    return {\n      status: SUCCESS_STATUS,\n    };\n  }\n}\n","sourceCodeStart":91,"sourceCodeEnd":117,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/calendars/services/ics-feed.service.ts#L91-L117","documentation":"The last guard in IcsFeedService.check: the connected calendar entry was found, but it carries an `error.message` (set by the calendar-integration layer when a live fetch against the feed failed: auth error, 404, parse error). That upstream message is forwarded verbatim inside UnauthorizedException, so the exact text depends on what the ICS adapter reported. This is a pass-through of the real upstream failure.","triggerScenarios":"The ICS feed is reachable but returns 401/403 (auth needed), the URL 404s, the feed body is not parseable ICS, or the upstream returned a transient 5xx that Cal recorded as the calendar's error state. The exact message is whatever the adapter set on connectedCalendar.error.message.","commonSituations":"Provider rotated the secret ICS link; public link became rate-limited; feed moved behind a login; TLS or DNS hiccup recorded as a permanent error in the connected-calendar row.","solutions":["Read the forwarded message string; it names the actual upstream problem (e.g. '401 Unauthorized', 'invalid ics').","Re-save with the current private ICS URL via POST /v2/calendars/ics/save to replace the failing credential.","Open the feed URL directly in a browser/curl to confirm it returns a valid .ics before re-saving."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const { connectedCalendars } = await calendarsService.getCalendars(userId);\nconst ics = connectedCalendars.find(c => c.integration.type === ICS_CALENDAR_TYPE);\nif (ics?.error?.message) {\n  // surface ics.error.message to the user and offer re-save\n}","typeGuard":null,"tryCatchPattern":"try {\n  await icsFeedService.check(userId);\n} catch (e) {\n  if (e instanceof UnauthorizedException) {\n    // e.message is the upstream cause; if it mentions auth/401, re-save the feed\n  } else throw e;\n}","preventionTips":["Monitor connectedCalendar.error.message in the UI and prompt re-save.","Keep ICS URLs current; rotate before the provider invalidates them.","Verify the feed URL still returns 200 text/calendar periodically."],"tags":["ics","calendar","upstream","api-v2"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}