{"record":{"id":"a66a5965919edd1f","repo":"calcom/cal.diy","slug":"could-not-add-ics-feeds-try-using-private-ics-fee","errorCode":null,"errorMessage":"Could not add ICS feeds, try using private ics feed.","messagePattern":"Could not add ICS feeds, try using private ics feed\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"apps/api/v2/src/platform/calendars/services/ics-feed.service.ts","lineNumber":82,"sourceCode":"        userId\n      );\n\n      await this.calendarsCacheService.deleteConnectedAndDestinationCalendarsCache(userId);\n\n      return {\n        status: SUCCESS_STATUS,\n        data: {\n          id: credential.id,\n          type: credential.type,\n          userId: credential.userId,\n          teamId: credential.teamId,\n          appId: credential.appId,\n          invalid: credential.invalid,\n        },\n      };\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);","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/calendars/services/ics-feed.service.ts#L64-L100","documentation":"The catch-all in IcsFeedService.save. Any exception thrown inside the try block (the listCalendars mismatch above, the BuildIcsFeedCalendarService construction, the symmetricEncrypt on an unset CALENDSO_ENCRYPTION_KEY, or the credentialRepository.upsertUserAppCredential call) is logged and re-thrown as BadRequestException('Could not add ICS feeds, try using private ics feed.'). Because the original error is only logged (logger.error) and not chained into the thrown exception, this message hides the root cause from the caller.","triggerScenarios":"The underlying throw is most often the listedCals/urls mismatch (error 381); secondarily a network failure inside listCalendars; third an empty/missing CALENDSO_ENCRYPTION_KEY making symmetricEncrypt produce an unusable credential key; finally a DB error during upsertUserAppCredential.","commonSituations":"Public ICS links that render HTML, intermittent upstream feed downtime, a fresh deployment where CALENDSO_ENCRYPTION_KEY is not yet set, or a feed URL that needs cookies/basic-auth the ICS adapter doesn't send.","solutions":["Check the server logs: the logger.error('Could not add ICS feeds', e) line right above contains the real exception; read it first.","Switch every URL in urls[] to the provider's private/secret ICS address (the message itself recommends this).","Verify process.env.CALENDSO_ENCRYPTION_KEY is set and non-empty on the API host, otherwise symmetricEncrypt silently breaks the stored credential.","Reproduce with a single URL to isolate the failing feed."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if (!process.env.CALENDSO_ENCRYPTION_KEY) {\n  throw new Error('CALENDSO_ENCRYPTION_KEY is not set; ICS save will silently fail.');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await icsFeedService.save(userId, userEmail, urls);\n} catch (e) {\n  if (e instanceof BadRequestException && e.message.includes('Could not add ICS feeds')) {\n    // read server log line 'Could not add ICS feeds' for the real cause; re-save with a private URL\n  } else throw e;\n}","preventionTips":["Set CALENDSO_ENCRYPTION_KEY on every API host before saving any ICS feed.","Switch to private ICS URLs as the error message suggests.","Correlate the 400 with the server-side logger.error line to recover the root cause."],"tags":["ics","calendar","catch-all","api-v2"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}