{"record":{"id":"91bdb0ba09624a95","repo":"calcom/cal.diy","slug":"unable-to-fetch-connected-calendars-events-please","errorCode":null,"errorMessage":"Unable to fetch connected calendars events. Please try again later.","messagePattern":"Unable to fetch connected calendars events\\. Please try again later\\.","errorType":"http","errorClass":"InternalServerErrorException","httpStatus":500,"severity":"error","filePath":"apps/api/v2/src/platform/calendars/services/calendars.service.ts","lineNumber":126,"sourceCode":"    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,\n      userId\n    );\n    const calendarBusyTimesQuery = await getBusyCalendarTimes(\n      this.buildNonDelegationCredentials(credentials),\n      dateFrom,\n      dateTo,\n      composedSelectedCalendars\n    );\n    if (!calendarBusyTimesQuery.success) {\n      throw new InternalServerErrorException(\n        \"Unable to fetch connected calendars events. Please try again later.\"\n      );\n    }\n    const calendarBusyTimesConverted = calendarBusyTimesQuery.data.map(\n      (busyTime: EventBusyDate & { timeZone?: string }) => {\n        const busyTimeStart = DateTime.fromJSDate(new Date(busyTime.start)).setZone(timezone);\n        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  }","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/calendars/services/calendars.service.ts#L108-L144","documentation":"Thrown by CalendarsService.getBusyTimes (calendars.service.ts:126) as InternalServerErrorException (HTTP 500) when getBusyCalendarTimes(...) returns { success: false }. The upstream Cal.com platform-libraries call could not fetch busy times from one or more providers; the underlying error detail is not propagated to the caller (diagnosability gap).","triggerScenarios":"Provider (Google/Microsoft/Apple) API outage or rate limit; an expired OAuth token causes a provider 401 surfaced as failure; malformed/huge date range; network timeout to the provider.","commonSituations":"Google Calendar API quota exceeded; token refresh failed inside the integration; transient network blip; very wide dateFrom/dateTo window.","solutions":["Retry with exponential backoff — many failures are transient provider hiccups.","Check provider status pages and verify the user's OAuth token validity (re-connect if revoked).","Narrow the dateFrom/dateTo range and reduce the number of calendars in calendarsToLoad.","Inspect server logs (platform-libraries integration) for the actual failure cause, since it is not in the response."],"exampleFix":"// before: single shot, surfaces only a generic 500\nconst times = await api.get('/v2/calendars/busy-times', { params });\n\n// after: bounded retry with backoff on 500\nasync function getBusyTimesSafe(params, attempts = 3) {\n  for (let i = 0; i < attempts; i++) {\n    try { return await api.get('/v2/calendars/busy-times', { params }); }\n    catch (e) {\n      if (e.response?.status !== 500 || i === attempts - 1) throw e;\n      await sleep(2 ** i * 500);\n    }\n  }\n}","handlingStrategy":"retry","validationCode":"// Reduce blast radius before calling busy-times\nconst safeCalendars = calendarsToLoad.filter(c => ownedIds.has(c.credentialId));\nconst clipped = {\n  ...params,\n  dateFrom: max(params.dateFrom, todayMinus(60)),\n  dateTo: min(params.dateTo, todayPlus(60)),\n  calendarsToLoad: safeCalendars,\n};\nawait api.get('/v2/calendars/busy-times', { params: clipped });","typeGuard":"function isBusyTimesSuccess(res) {\n  return res?.success === true && Array.isArray(res?.data);\n}","tryCatchPattern":"async function getBusyTimesRetry(params, attempts = 3) {\n  for (let i = 0; i < attempts; i++) {\n    try {\n      return await api.get('/v2/calendars/busy-times', { params });\n    } catch (e) {\n      if (e.response?.status !== 500 || i === attempts - 1) throw e;\n      await new Promise(r => setTimeout(r, 2 ** i * 500));\n    }\n  }\n}","preventionTips":["Retry 500 busy-times failures with exponential backoff — many are transient.","Keep dateFrom/dateTo ranges modest to avoid provider timeouts/quota.","Log the underlying integration error server-side; the HTTP message intentionally hides it."],"tags":["busy-times","upstream","provider","retry","http-500"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}