{"record":{"id":"6c6e2acdfb249d97","repo":"calcom/cal.diy","slug":"invalid-time-range-given-check-the-start-and","errorCode":null,"errorMessage":"Invalid time range given - check the 'start' and 'end' query parameters.","messagePattern":"Invalid time range given - check the 'start' and 'end' query parameters\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"apps/api/v2/src/modules/slots/slots-2024-09-04/services/slots.service.ts","lineNumber":76,"sourceCode":"    try {\n      const availableSlots: TimeSlots = await this.availableSlotsService.getAvailableSlots({\n        input: queryTransformed,\n        ctx: {},\n      });\n\n      const formatted = await this.slotsOutputService.getAvailableSlots(\n        availableSlots,\n        queryTransformed.eventTypeId,\n        queryTransformed.duration,\n        format,\n        queryTransformed.timeZone\n      );\n\n      return formatted;\n    } catch (error) {\n      if (error instanceof Error) {\n        if (error.message.includes(\"Invalid time range given\")) {\n          throw new BadRequestException(\n            \"Invalid time range given - check the 'start' and 'end' query parameters.\"\n          );\n        }\n      }\n      throw error;\n    }\n  }\n\n  async getAvailableSlots(query: GetSlotsInput_2024_09_04) {\n    const queryTransformed = await this.slotsInputService.transformGetSlotsQuery(query);\n    return this.fetchAndFormatSlots(queryTransformed, query.format);\n  }\n\n  async getAvailableSlotsWithRouting(query: GetSlotsInputWithRouting_2024_09_04) {\n    const queryTransformed = await this.slotsInputService.transformRoutingGetSlotsQuery(query);\n    return this.fetchAndFormatSlots(queryTransformed, query.format);\n  }\n","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/slots/slots-2024-09-04/services/slots.service.ts#L58-L94","documentation":"A NestJS BadRequestException (HTTP 400) from SlotsService_2024_09_04.fetchAndFormatSlots. It wraps any error from AvailableSlotsService whose message includes 'Invalid time range given' into a stable client-facing message pointing at the start/end query params. The underlying cause is that the availability engine rejected the window — typically start is on or after end, or the range exceeds the allowed lookup span.","triggerScenarios":"GET /v2/slots where start >= end; start and end are identical; the window (end - start) exceeds the configured maximum slot lookup range (often 30/45/180 days); start/end are valid ISO but semantically inverted.","commonSituations":"Defaulting end to the same value as start; flipping date pickers; requesting a full year of slots; timezone conversion on the client pushing start past end; reusing a cached end that is now in the past.","solutions":["Ensure end is strictly greater than start before the request.","Cap the window to the platform's allowed lookup span (default is typically 30 days; trim end accordingly).","When offering a date range UI, enforce start <= end and a max span client-side.","If you need a longer view, page through multiple requests with offsets."],"exampleFix":"// before\nconst start = DateTime.now().toISO();\nconst end = DateTime.now().toISO(); // same → invalid\n\n// after\nconst start = DateTime.now().toISO()!;\nconst end = DateTime.now().plus({ days: 30 }).toISO()!;\nfetch(`/v2/slots?start=${encodeURIComponent(start)}&end=${encodeURIComponent(end)}`);","handlingStrategy":"validation","validationCode":"import { DateTime } from 'luxon';\n\nconst MAX_LOOKUP_DAYS = 30;\nfunction assertValidRange(startISO: unknown, endISO: unknown): { start: string; end: string } {\n  const start = DateTime.fromISO(String(startISO), { zone: 'utc' });\n  const end = DateTime.fromISO(String(endISO), { zone: 'utc' });\n  if (!start.isValid || !end.isValid) throw new RangeError('start/end must be valid ISO');\n  if (end.toMillis() <= start.toMillis()) throw new RangeError('end must be strictly after start');\n  if (end.diff(start, 'days').days > MAX_LOOKUP_DAYS) throw new RangeError(`window must be <= ${MAX_LOOKUP_DAYS} days`);\n  return { start: start.toISO()!, end: end.toISO()! };\n}","typeGuard":"function isValidRange(startISO: unknown, endISO: unknown): startISO is string {\n  if (typeof startISO !== 'string' || typeof endISO !== 'string') return false;\n  const s = DateTime.fromISO(startISO, { zone: 'utc' });\n  const e = DateTime.fromISO(endISO, { zone: 'utc' });\n  return s.isValid && e.isValid && e.toMillis() > s.toMillis();\n}","tryCatchPattern":"try {\n  await cal.slots.list({ start, end, ... });\n} catch (e) {\n  if (e instanceof HttpError && e.statusCode === 400 && /Invalid time range/i.test(e.message)) {\n    // correct the window and retry once\n    const { start: s, end: en } = clampWindow(start, end);\n    return cal.slots.list({ start: s, end: en, ... });\n  }\n  throw e;\n}","preventionTips":["Enforce start < end on the client before the request.","Cap the lookup window to the platform maximum (default ~30 days).","Page long ranges with multiple smaller requests.","Re-derive end from start + N days rather than caching it."],"tags":["calcom-api","slots","bad-request","time-range","query-validation"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}