{"record":{"id":"6663868d06131da8","repo":"calcom/cal.diy","slug":"calendar-connection-not-found-666386","errorCode":null,"errorMessage":"Calendar connection not found","messagePattern":"Calendar connection not found","errorType":"http","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"apps/api/v2/src/platform/calendars/services/calendars.service.ts","lineNumber":98,"sourceCode":"    });\n    await this.calendarsCacheService.setConnectedAndDestinationCalendarsCache(userId, result);\n\n    return result;\n  }\n\n  /**\n   * Delegates to getCalendars() (which is cached by CalendarsCacheService) because the\n   * upstream platform-libraries API only supports fetching all credentials at once —\n   * a targeted single-credential query is tracked as a future optimisation.\n   */\n  async getCalendarsForConnection(\n    userId: number,\n    credentialId: number\n  ): Promise<ConnectedDestinationCalendars> {\n    const full = await this.getCalendars(userId);\n    const conn = full.connectedCalendars.find((c) => c.credentialId === credentialId);\n    if (!conn) {\n      throw new NotFoundException(\"Calendar connection not found\");\n    }\n    return {\n      ...full,\n      connectedCalendars: [conn],\n    };\n  }\n\n  async getBusyTimes(\n    calendarsToLoad: Calendar[],\n    userId: User[\"id\"],\n    dateFrom: string,\n    dateTo: string,\n    timezone: string\n  ) {\n    const credentials = await this.getUniqCalendarCredentials(calendarsToLoad, userId);\n    const composedSelectedCalendars = await this.getCalendarsWithCredentials(\n      credentials,\n      calendarsToLoad,","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/calendars/services/calendars.service.ts#L80-L116","documentation":"Thrown by CalendarsService.getCalendarsForConnection (calendars.service.ts:98) as NotFoundException (HTTP 404) when, after calling getCalendars(userId), no entry in connectedCalendars matches credentialId === <arg>. The credentialId either does not exist, belongs to another user, or is not a calendar credential. The method delegates to getCalendars (cached) because no targeted single-credential query exists yet.","triggerScenarios":"GET /v2/calendars/connections/{credentialId} (or the per-connection endpoint) with an id not owned by the user; credential deleted between list and detail; cache stale and missing the entry.","commonSituations":"Client holds a stale credentialId after a disconnect; typo or copy-paste of an id; cross-user id from another account.","solutions":["Refresh the calendar list via GET /v2/calendars and use only a credentialId present in the response.","If you just connected, invalidate the connected-calendars cache (server-side) before retrying.","Confirm the credential belongs to the current user with checkCalendarCredentials(credentialId, userId)."],"exampleFix":"// before: passing an unverified credentialId\nawait api.get(`/v2/calendars/connections/${maybeStaleId}`); // 404\n\n// after: source the id from the current list\nconst { data } = await api.get('/v2/calendars');\nconst validId = data.connectedCalendars.find(c => c.credentialId === maybeStaleId)?.credentialId;\nif (!validId) throw new ClientError('credential not owned');\nawait api.get(`/v2/calendars/connections/${validId}`);","handlingStrategy":"validation","validationCode":"// Source credentialId from the current list before calling the per-connection endpoint\nconst { data } = await api.get('/v2/calendars');\nconst owned = data.connectedCalendars.find(c => c.credentialId === credentialId);\nif (!owned) throw new ClientError('credentialId not in your connected calendars');\nawait api.get(`/v2/calendars/connections/${credentialId}`);","typeGuard":"function isOwnedCredentialId(list, credentialId) {\n  return Array.isArray(list) && list.some((c) => c?.credentialId === credentialId);\n}","tryCatchPattern":"try {\n  await api.get(`/v2/calendars/connections/${credentialId}`);\n} catch (e) {\n  if (e.response?.status === 404) {\n    // refresh the list and retry with a valid id, or treat as disconnected\n    await refreshCalendarList();\n    return;\n  }\n  throw e;\n}","preventionTips":["Always derive credentialId from GET /v2/calendars for the current user, never from cached/external state.","Invalidate the connected-calendars cache after connect/disconnect so the list is fresh.","Treat 404 on the per-connection endpoint as 'not owned or already disconnected'."],"tags":["calendar-connection","not-found","credentials","http-404"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}