{"record":{"id":"0cdd48f63e28dee5","repo":"calcom/cal.diy","slug":"invalid-slot-format-must-be-either-range-or-ti","errorCode":null,"errorMessage":"Invalid slot format. Must be either 'range' or 'time'","messagePattern":"Invalid slot format\\. Must be either 'range' or 'time'","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"apps/api/v2/src/modules/slots/slots-2024-04-15/services/slots-output.service.ts","lineNumber":67,"sourceCode":"        startTime: DateTime.fromISO(slot.startTime).setZone(timeZone).toISO() || \"unknown-start-time\",\n        endTime: DateTime.fromISO(slot.endTime).setZone(timeZone).toISO() || \"unknown-end-time\",\n        ...(slot.attendees ? { attendees: slot.attendees } : {}),\n        ...(slot.bookingUid ? { bookingUid: slot.bookingUid } : {}),\n      }));\n      return acc;\n    }, {} as Record<string, { startTime: string; endTime: string }[]>);\n\n    return { slots: formattedSlots };\n  }\n\n  private async formatSlots(\n    availableSlots: TimeSlots,\n    duration?: number,\n    eventTypeId?: number,\n    slotFormat?: SlotFormat\n  ): Promise<RangeSlots> {\n    if (slotFormat && !Object.values(SlotFormat).includes(slotFormat)) {\n      throw new BadRequestException(\"Invalid slot format. Must be either 'range' or 'time'\");\n    }\n\n    const slotDuration = await this.getDuration(duration, eventTypeId);\n\n    const slots = Object.entries(availableSlots.slots).reduce<\n      Record<string, { startTime: string; endTime: string; attendees?: number; bookingUid?: string }[]>\n    >((acc, [date, slots]) => {\n      acc[date] = (slots as { time: string; attendees?: number; bookingUid?: string }[]).map((slot) => {\n        const startTime = new Date(slot.time);\n        const endTime = new Date(startTime.getTime() + slotDuration * 60000);\n        return {\n          startTime: startTime.toISOString(),\n          endTime: endTime.toISOString(),\n          ...(slot.attendees ? { attendees: slot.attendees } : {}),\n          ...(slot.bookingUid ? { bookingUid: slot.bookingUid } : {}),\n        };\n      });\n      return acc;","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/slots/slots-2024-04-15/services/slots-output.service.ts#L49-L85","documentation":"BadRequestException (HTTP 400) thrown by SlotsOutputService.formatSlots() when the slotFormat query parameter is supplied but is not one of the SlotFormat enum values ('range' or 'time'). The guard runs Object.values(SlotFormat).includes(slotFormat); an unknown string is rejected. Omitting slotFormat entirely is allowed (defaults apply).","triggerScenarios":"GET /v2/slots/2024-04-15?slotFormat=ranges (typo/plural), slotFormat=range-time, slotFormat=friendly, or any casing mismatch like 'Range' if the enum is lowercase and case-sensitive.","commonSituations":"Client hardcoding a format string that drifted from the enum; a frontend toggle with a label value instead of the enum value; an integration guessing the format name; uppercase vs lowercase mismatch.","solutions":["Send slotFormat exactly as 'range' or 'time' (verify against the SlotFormat enum in the types package).","Omit slotFormat if you want the default rather than guessing.","Drive the value from a shared enum/const rather than a free-text string in the client."],"exampleFix":"// before\napi.get('/slots/2024-04-15', { slotFormat: 'ranges' }); // typo\n// after — use the shared enum\nimport { SlotFormat } from '@calcom/types';\napi.get('/slots/2024-04-15', { slotFormat: SlotFormat.Range }); // 'range'","handlingStrategy":"type-guard","validationCode":"const SLOT_FORMATS = ['range', 'time'] as const;\ntype SlotFormat = typeof SLOT_FORMATS[number];\nfunction toSlotFormat(v: string): SlotFormat {\n  if (!SLOT_FORMATS.includes(v as SlotFormat)) throw new Error(`slotFormat must be one of ${SLOT_FORMATS.join(', ')}`);\n  return v as SlotFormat;\n}","typeGuard":"const isSlotFormat = (v: unknown): v is 'range' | 'time' =>\n  v === 'range' || v === 'time';","tryCatchPattern":null,"preventionTips":["Bind the client's format picker to the SlotFormat enum, not free text.","Omit slotFormat to use the default rather than guessing a string.","Write the enum value in lowercase exactly as the server defines it."],"tags":["slots","enum-validation","query-validation","nestjs","api-v2"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}