calcom/cal.diy · error · BadRequestException

Invalid slot format. Must be either 'range' or 'time'

Error message

Invalid slot format. Must be either 'range' or 'time'

What it means

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).

Source

Thrown at apps/api/v2/src/modules/slots/slots-2024-04-15/services/slots-output.service.ts:67

        startTime: DateTime.fromISO(slot.startTime).setZone(timeZone).toISO() || "unknown-start-time",
        endTime: DateTime.fromISO(slot.endTime).setZone(timeZone).toISO() || "unknown-end-time",
        ...(slot.attendees ? { attendees: slot.attendees } : {}),
        ...(slot.bookingUid ? { bookingUid: slot.bookingUid } : {}),
      }));
      return acc;
    }, {} as Record<string, { startTime: string; endTime: string }[]>);

    return { slots: formattedSlots };
  }

  private async formatSlots(
    availableSlots: TimeSlots,
    duration?: number,
    eventTypeId?: number,
    slotFormat?: SlotFormat
  ): Promise<RangeSlots> {
    if (slotFormat && !Object.values(SlotFormat).includes(slotFormat)) {
      throw new BadRequestException("Invalid slot format. Must be either 'range' or 'time'");
    }

    const slotDuration = await this.getDuration(duration, eventTypeId);

    const slots = Object.entries(availableSlots.slots).reduce<
      Record<string, { startTime: string; endTime: string; attendees?: number; bookingUid?: string }[]>
    >((acc, [date, slots]) => {
      acc[date] = (slots as { time: string; attendees?: number; bookingUid?: string }[]).map((slot) => {
        const startTime = new Date(slot.time);
        const endTime = new Date(startTime.getTime() + slotDuration * 60000);
        return {
          startTime: startTime.toISOString(),
          endTime: endTime.toISOString(),
          ...(slot.attendees ? { attendees: slot.attendees } : {}),
          ...(slot.bookingUid ? { bookingUid: slot.bookingUid } : {}),
        };
      });
      return acc;

View on GitHub (pinned to 176037d0af)

Solutions

  1. Send slotFormat exactly as 'range' or 'time' (verify against the SlotFormat enum in the types package).
  2. Omit slotFormat if you want the default rather than guessing.
  3. Drive the value from a shared enum/const rather than a free-text string in the client.

Example fix

// before
api.get('/slots/2024-04-15', { slotFormat: 'ranges' }); // typo
// after — use the shared enum
import { SlotFormat } from '@calcom/types';
api.get('/slots/2024-04-15', { slotFormat: SlotFormat.Range }); // 'range'
Defensive patterns

Strategy: type-guard

Validate before calling

const SLOT_FORMATS = ['range', 'time'] as const;
type SlotFormat = typeof SLOT_FORMATS[number];
function toSlotFormat(v: string): SlotFormat {
  if (!SLOT_FORMATS.includes(v as SlotFormat)) throw new Error(`slotFormat must be one of ${SLOT_FORMATS.join(', ')}`);
  return v as SlotFormat;
}

Type guard

const isSlotFormat = (v: unknown): v is 'range' | 'time' =>
  v === 'range' || v === 'time';

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12). Data as JSON: /api/errors/0cdd48f63e28dee5. Report an issue: GitHub.