{"record":{"id":"cd92334c02c6a49b","repo":"calcom/cal.diy","slug":"invalid-ics-feed-credentials","errorCode":null,"errorMessage":"Invalid Ics Feed credentials.","messagePattern":"Invalid Ics Feed credentials\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"apps/api/v2/src/platform/calendars/services/ics-feed.service.ts","lineNumber":97,"sourceCode":"      };\n    } catch (e) {\n      this.logger.error(\"Could not add ICS feeds\", e);\n      throw new BadRequestException(\"Could not add ICS feeds, try using private ics feed.\");\n    }\n  }\n\n  async check(userId: number): Promise<{ status: typeof SUCCESS_STATUS }> {\n    const icsFeedCredentials = await this.credentialRepository.findCredentialByTypeAndUserId(\n      ICS_CALENDAR_TYPE,\n      userId\n    );\n\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  }","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/calendars/services/ics-feed.service.ts#L79-L115","documentation":"In IcsFeedService.check, after the credential row is found it is inspected for an `invalid` flag. Cal.com marks a credential invalid when a prior calendar operation failed (e.g. a refresh token revoked upstream, a feed that returned auth errors repeatedly). A truthy `invalid` short-circuits with BadRequestException('Invalid Ics Feed credentials.') instead of attempting the live getCalendars call.","triggerScenarios":"The ICS feed URL changed, was made private, or was deleted on the provider side after the initial save; an earlier operation hit the credential and set its `invalid` column to true; the encryption key was rotated so the stored credential key no longer decrypts correctly and the resulting fetch fails enough times to flip invalid.","commonSituations":"User revoked/deleted the source calendar in Google/Outlook; CALENDSO_ENCRYPTION_KEY was rotated without re-encrypting existing credentials; long-lived credential whose feed moved behind auth.","solutions":["Re-save the ICS feed via POST /v2/calendars/ics/save with a fresh, valid URL; upsert will replace the credential and clear invalid.","In the webapp, remove the broken ICS integration and re-add it so a brand-new credential row is created.","If invalid flipped due to an encryption-key rotation, re-encrypt or reset the affected credential rows rather than re-adding them one by one."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const cred = await credentialRepository.findCredentialByTypeAndUserId(ICS_CALENDAR_TYPE, userId);\nif (cred?.invalid) {\n  // prompt re-save with a fresh URL rather than calling /check\n}","typeGuard":null,"tryCatchPattern":"try {\n  await icsFeedService.check(userId);\n} catch (e) {\n  if (e instanceof BadRequestException && e.message === 'Invalid Ics Feed credentials.') {\n    await icsFeedService.save(userId, userEmail, freshUrls); // re-save\n  } else throw e;\n}","preventionTips":["Re-save the ICS feed whenever the source calendar is moved or re-secured.","Do not rotate CALENDSO_ENCRYPTION_KEY without re-encrypting existing credentials.","Surface 'invalid' state in the UI so users re-add proactively."],"tags":["ics","calendar","credentials","api-v2"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}