calcom/cal.diy · error · BadRequestException

You passed 'slotDuration' but this event type is not a varia

Error message

You passed 'slotDuration' but this event type is not a variable length event type.

What it means

A NestJS BadRequestException (HTTP 400) from SlotsService_2024_09_04.validateSlotDuration. The caller passed slotDuration, but eventType.metadata.multipleDuration is absent/falsy. slotDuration is only valid for variable-length event types that declare a multipleDuration array in their metadata. A fixed-length event type rejects an explicit duration.

Source

Thrown at apps/api/v2/src/modules/slots/slots-2024-09-04/services/slots.service.ts:219

  private async checkSlotOverlap(eventTypeId: number, startDate: string, endDate: string) {
    const overlappingReservation = await this.slotsRepository.getOverlappingSlotReservation(
      eventTypeId,
      startDate,
      endDate
    );

    if (overlappingReservation) {
      throw new UnprocessableEntityException(
        `This time slot is already reserved by another user. Please choose a different time.`
      );
    }
  }

  validateSlotDuration(eventType: EventType, inputSlotDuration: number) {
    const eventTypeMetadata = eventTypeMetadataSchema.parse(eventType.metadata);
    if (!eventTypeMetadata?.multipleDuration) {
      throw new BadRequestException(
        "You passed 'slotDuration' but this event type is not a variable length event type."
      );
    }

    if (!eventTypeMetadata.multipleDuration.includes(inputSlotDuration)) {
      throw new BadRequestException(
        `Provided 'slotDuration' is not one of the possible lengths for the event type. The possible lengths for this variable length event type are: ${eventTypeMetadata.multipleDuration.join(
          ", "
        )}`
      );
    }
  }

  async canSpecifyCustomReservationDuration(authUserId: number, eventType: EventType) {
    if (eventType.userId) {
      return await this.canSpecifyCustomReservationDurationIndividualEvent(authUserId, eventType.userId);
    }
    if (eventType.teamId) {

View on GitHub (pinned to 176037d0af)

Solutions

  1. Only send slotDuration for event types whose metadata.multipleDuration is a non-empty array.
  2. Inspect the event type via GET /v2/event-types and branch on whether it is variable-length.
  3. For fixed-length event types, omit slotDuration and let eventType.length apply.
  4. On the client, gate the slotDuration field behind a 'variable length' flag.

Example fix

// before
fetch('/v2/slots/reserve', { body: JSON.stringify({ eventTypeId, slotStart, slotDuration: 30 }) });

// after — only include slotDuration for variable-length events
const body: any = { eventTypeId, slotStart };
if (eventType.metadata?.multipleDuration?.length) body.slotDuration = 30;
fetch('/v2/slots/reserve', { body: JSON.stringify(body) });
Defensive patterns

Strategy: validation

Validate before calling

function buildReserveBody(eventType: { metadata?: { multipleDuration?: number[] } | null }, slotStart: string, slotDuration?: number) {
  const body: { eventTypeId?: number; slotStart: string; slotDuration?: number } = { slotStart };
  if (eventType.metadata?.multipleDuration?.length && slotDuration != null) {
    if (!eventType.metadata.multipleDuration.includes(slotDuration)) {
      throw new RangeError(`slotDuration must be one of: ${eventType.metadata.multipleDuration.join(', ')}`);
    }
    body.slotDuration = slotDuration;
  }
  return body;
}
const body = buildReserveBody(eventType, slotStart, input.slotDuration);

Type guard

function isVariableLengthEventType(et: { metadata?: { multipleDuration?: number[] } | null }): boolean {
  return Array.isArray(et.metadata?.multipleDuration) && (et.metadata!.multipleDuration as number[]).length > 0;
}

Try / catch

try {
  await cal.slots.reserve({ eventTypeId, slotStart, slotDuration });
} catch (e) {
  if (e instanceof HttpError && e.statusCode === 400 && /variable length/i.test(e.message)) {
    // event type is fixed-length — retry without slotDuration
    return cal.slots.reserve({ eventTypeId, slotStart });
  }
  throw e;
}

Prevention

When it happens

Trigger: POST /v2/slots/reserve with a slotDuration field for an event type whose metadata does not include multipleDuration — i.e. a standard fixed-length event type. The DTO allows slotDuration optionally, so this is the runtime semantic check.

Common situations: Applying slotDuration uniformly across all event types in a generic booking flow; switching an event type from variable to fixed and leaving stale client code; copying a reserve payload from a variable event to a fixed one.

Related errors


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