{"record":{"id":"0589bd2832e3f797","repo":"calcom/cal.diy","slug":"these-credentials-do-not-belong-to-you","errorCode":null,"errorMessage":"These credentials do not belong to you","messagePattern":"These credentials do not belong to you","errorType":"http","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"apps/api/v2/src/platform/calendars/services/calendars.service.ts","lineNumber":151,"sourceCode":"        const busyTimeEnd = DateTime.fromJSDate(new Date(busyTime.end)).setZone(timezone);\n        const busyTimeStartDate = busyTimeStart.toJSDate();\n        const busyTimeEndDate = busyTimeEnd.toJSDate();\n        return {\n          ...busyTime,\n          start: busyTimeStartDate,\n          end: busyTimeEndDate,\n        };\n      }\n    );\n    return calendarBusyTimesConverted;\n  }\n\n  async getUniqCalendarCredentials(calendarsToLoad: Calendar[], userId: User[\"id\"]) {\n    const uniqueCredentialIds = Array.from(new Set(calendarsToLoad.map((item) => item.credentialId)));\n    const credentials = await this.credentialsRepository.getUserCredentialsByIds(userId, uniqueCredentialIds);\n\n    if (credentials.length !== uniqueCredentialIds.length) {\n      throw new UnauthorizedException(\"These credentials do not belong to you\");\n    }\n\n    return credentials;\n  }\n\n  async getCalendarsWithCredentials(\n    credentials: CredentialsWithUserEmail,\n    calendarsToLoad: Calendar[],\n    userId: User[\"id\"]\n  ) {\n    const composedSelectedCalendars = calendarsToLoad.map((calendar) => {\n      const credential = credentials.find((item) => item.id === calendar.credentialId);\n      if (!credential) {\n        throw new UnauthorizedException(\"These credentials do not belong to you\");\n      }\n      return {\n        ...calendar,\n        userId,","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/calendars/services/calendars.service.ts#L133-L169","documentation":"Thrown by CalendarsService.getUniqCalendarCredentials (calendars.service.ts:151) as UnauthorizedException (HTTP 401) when getUserCredentialsByIds(userId, uniqueCredentialIds) returns fewer rows than the number of unique credentialIds requested. At least one credentialId in calendarsToLoad is absent or belongs to another user (the query filters by userId).","triggerScenarios":"GET /v2/calendars/busy-times with a calendarsToLoad[].credentialId belonging to another user; a deleted credential id; a fabricated/guessed id; stale id cached client-side after re-connecting a different account.","commonSituations":"Client caches credentialIds across account switches; multi-tenant mix-up; frontend retained an id after the user re-authenticated as someone else.","solutions":["Only pass credentialIds returned by GET /v2/calendars for the currently authenticated user.","Filter calendarsToLoad client-side to ids present in the user's connectedCalendars before calling busy-times.","Treat a 401 here as a security signal — do not silently retry with the same ids."],"exampleFix":"// before: passing whatever ids the client holds\nawait api.get('/v2/calendars/busy-times', { params: { calendarsToLoad } }); // 401\n\n// after: intersect with the user's own credential ids\nconst { data } = await api.get('/v2/calendars');\nconst owned = new Set(data.connectedCalendars.map(c => c.credentialId));\nconst safe = calendarsToLoad.filter(c => owned.has(c.credentialId));\nawait api.get('/v2/calendars/busy-times', { params: { calendarsToLoad: safe } });","handlingStrategy":"validation","validationCode":"// Intersect requested ids with the user's own credentials before busy-times\nconst { data } = await api.get('/v2/calendars');\nconst owned = new Set(data.connectedCalendars.map(c => c.credentialId));\nconst safe = calendarsToLoad.filter(c => owned.has(c.credentialId));\nif (safe.length !== calendarsToLoad.length) {\n  throw new Error('Some credentialIds are not owned by this user');\n}\nawait api.get('/v2/calendars/busy-times', { params: { ...params, calendarsToLoad: safe } });","typeGuard":"function allOwned(requestedIds, ownedIds) {\n  return requestedIds.every((id) => ownedIds.has(id));\n}","tryCatchPattern":"try {\n  await api.get('/v2/calendars/busy-times', { params });\n} catch (e) {\n  if (e.response?.status === 401 && /do not belong/i.test(e.response?.data?.message)) {\n    // security signal — do NOT retry with the same ids; refresh ownership\n    await refreshCalendarList();\n    throw new AuthorizationError('credentialId ownership mismatch');\n  }\n  throw e;\n}","preventionTips":["Only pass credentialIds returned by GET /v2/calendars for the current user.","Treat a 401 'do not belong to you' as a security event — never silently retry.","Clear cached credentialIds on logout/account switch."],"tags":["authorization","credentials","busy-times","http-401"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}