{"record":{"id":"543da94b456933db","repo":"calcom/cal.diy","slug":"invalid-start-date-543da9","errorCode":null,"errorMessage":"Invalid start date","messagePattern":"Invalid start date","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"apps/api/v2/src/modules/slots/slots-2024-09-04/services/slots.service.ts","lineNumber":121,"sourceCode":"    if (!eventType) {\n      throw new NotFoundException(`Event Type with ID=${input.eventTypeId} not found`);\n    }\n\n    if (input.reservationDuration && authUserId) {\n      const canSpecifyCustomReservationDuration = await this.canSpecifyCustomReservationDuration(\n        authUserId,\n        eventType\n      );\n      if (!canSpecifyCustomReservationDuration) {\n        throw new ForbiddenException(\n          \"authenticated user is not owner of event type, does not have memberships in common with owner of the event type, nor does belong to event type's team or org.\"\n        );\n      }\n    }\n\n    const startDate = DateTime.fromISO(input.slotStart, { zone: \"utc\" });\n    if (!startDate.isValid) {\n      throw new BadRequestException(\"Invalid start date\");\n    }\n\n    if (input.slotDuration) {\n      this.validateSlotDuration(eventType, input.slotDuration);\n    }\n\n    const endDate = startDate.plus({ minutes: input.slotDuration ?? eventType.length });\n    if (!endDate.isValid) {\n      throw new BadRequestException(\"Invalid end date\");\n    }\n\n    const booking = await this.slotsRepository.findActiveOverlappingBooking(\n      input.eventTypeId,\n      startDate.toJSDate(),\n      endDate.toJSDate()\n    );\n\n    if (eventType.seatsPerTimeSlot) {","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/slots/slots-2024-09-04/services/slots.service.ts#L103-L139","documentation":"A NestJS BadRequestException (HTTP 400) from SlotsService_2024_09_04.reserveSlot. Luxon's DateTime.fromISO(input.slotStart, {zone:'utc'}) produced an invalid DateTime (.isValid === false). The ReserveSlotInput DTO's @IsDateString may accept strings that luxon still rejects, so this is the runtime backstop. The slotStart must be a real instant.","triggerScenarios":"POST /v2/slots/reserve with a slotStart that passes class-validator's date-string check but fails luxon parsing — e.g. a date-only '2024-09-04', an out-of-range component like month 13, or a string with invalid separators.","commonSituations":"Client sending a date without time; non-UTC offset that luxon disallows; copy-paste truncating the timestamp; passing a number coerced to string.","solutions":["Send slotStart as a full ISO 8601 UTC timestamp (new Date().toISOString()), e.g. '2024-09-04T09:00:00.000Z'.","Client-side assert DateTime.fromISO(slotStart, {zone:'utc'}).isValid before posting.","Use a slot start that was actually returned by GET /v2/slots rather than a hand-built string.","Verify the value survives JSON serialization unchanged."],"exampleFix":"// before\nfetch('/v2/slots/reserve', { body: JSON.stringify({ eventTypeId, slotStart: '2024-09-04 09:00' }) });\n\n// after\nconst slotStart = DateTime.fromISO('2024-09-04T09:00:00', { zone:'utc' }).toISO();\nif (!slotStart) throw new Error('bad slotStart');\nfetch('/v2/slots/reserve', { body: JSON.stringify({ eventTypeId, slotStart }) });","handlingStrategy":"validation","validationCode":"import { DateTime } from 'luxon';\n\nfunction toValidSlotStart(slotStart: unknown): string {\n  if (typeof slotStart !== 'string') throw new TypeError('slotStart must be an ISO string');\n  const dt = DateTime.fromISO(slotStart, { zone: 'utc' });\n  if (!dt.isValid) throw new RangeError(`Invalid slotStart: ${dt.invalidReason}`);\n  return dt.toISO()!;\n}\nconst slotStart = toValidSlotStart(input.slotStart);","typeGuard":"function isValidSlotStart(v: unknown): v is string {\n  return typeof v === 'string' && DateTime.fromISO(v, { zone: 'utc' }).isValid;\n}","tryCatchPattern":"try {\n  await cal.slots.reserve({ eventTypeId, slotStart });\n} catch (e) {\n  if (e instanceof HttpError && e.statusCode === 400 && /start date/i.test(e.message)) {\n    throw new UserFacingError('Please choose a valid time slot.');\n  }\n  throw e;\n}","preventionTips":["Always source slotStart from a GET /v2/slots response value.","Send full ISO 8601 UTC strings (new Date().toISOString()).","Run DateTime.fromISO(...).isValid before posting.","Do not pass bare dates or locale-formatted strings."],"tags":["calcom-api","slots","bad-request","luxon","datetime","reservation"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}