calcom/cal.diy · error · UnprocessableEntityException

Booking with id=${input.eventTypeId} at ${input.slotStart} h

Error message

Booking with id=${input.eventTypeId} at ${input.slotStart} has no more seats left.

What it means

A NestJS UnprocessableEntityException (HTTP 422) from SlotsService_2024_09_04.reserveSlot. The event type is seated (seatsPerTimeSlot set), an overlapping booking already exists, and the remaining seats (seatsPerTimeSlot - attendees) is less than 1. The reservation is semantically valid but the slot is full.

Source

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

    }

    const endDate = startDate.plus({ minutes: input.slotDuration ?? eventType.length });
    if (!endDate.isValid) {
      throw new BadRequestException("Invalid end date");
    }

    const booking = await this.slotsRepository.findActiveOverlappingBooking(
      input.eventTypeId,
      startDate.toJSDate(),
      endDate.toJSDate()
    );

    if (eventType.seatsPerTimeSlot) {
      const attendeesCount = booking?.attendees?.length;
      if (attendeesCount) {
        const seatsLeft = eventType.seatsPerTimeSlot - attendeesCount;
        if (seatsLeft < 1) {
          throw new UnprocessableEntityException(
            `Booking with id=${input.eventTypeId} at ${input.slotStart} has no more seats left.`
          );
        }
      }
    }

    const nonSeatedEventAlreadyBooked = !eventType.seatsPerTimeSlot && booking;
    const isRoundRobinEvent = eventType.schedulingType === SchedulingType.ROUND_ROBIN;

    if (nonSeatedEventAlreadyBooked && !isRoundRobinEvent) {
      throw new UnprocessableEntityException(`Can't reserve a slot if the event is already booked.`);
    }

    if (isRoundRobinEvent) {
      try {
        await validateRoundRobinSlotAvailability(input.eventTypeId, startDate, endDate, eventType.hosts);
      } catch (error) {
        if (error instanceof Error) {

View on GitHub (pinned to 176037d0af)

Solutions

  1. Re-query GET /v2/slots for the seated event and pick a slot with seatsRemaining > 0.
  2. Surface a 'fully booked' message and offer alternative times.
  3. Retry on a different slot rather than the same slotStart.
  4. Increase seatsPerTimeSlot on the event type if more capacity is intended.

Example fix

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

// after — pick an open seat from availability first
const slots = await calApi.slots.list({ eventTypeId: seatedEtId, ... });
const open = slots.find(s => s.seatsRemaining && s.seatsRemaining > 0);
if (!open) throw new ClientError('No seats available');
fetch('/v2/slots/reserve', { body: JSON.stringify({ eventTypeId: seatedEtId, slotStart: open.start }) });
Defensive patterns

Strategy: try-catch

Validate before calling

async function findOpenSeatedSlot(eventTypeId: number, window: { start: string; end: string }) {
  const slots = await cal.slots.list({ eventTypeId, ...window });
  const open = Object.values(slots).flat().find(s => typeof s.seatsRemaining === 'number' && s.seatsRemaining > 0);
  if (!open) throw new UserFacingError('All seats are booked — pick another time.');
  return open;
}
const open = await findOpenSeatedSlot(eventTypeId, { start, end });

Type guard

function hasOpenSeats(slot: unknown): slot is { start: string; seatsRemaining: number } {
  return typeof slot === 'object' && slot !== null
    && typeof (slot as any).start === 'string'
    && typeof (slot as any).seatsRemaining === 'number'
    && (slot as any).seatsRemaining > 0;
}

Try / catch

try {
  await cal.slots.reserve({ eventTypeId, slotStart });
} catch (e) {
  if (e instanceof HttpError && e.statusCode === 422 && /no more seats/i.test(e.message)) {
    const next = await findOpenSeatedSlot(eventTypeId, window);
    return cal.slots.reserve({ eventTypeId, slotStart: next.start });
  }
  throw e;
}

Prevention

When it happens

Trigger: POST /v2/slots/reserve for a seated event type where the existing booking at that time already has attendees equal to or exceeding seatsPerTimeSlot.

Common situations: Two attendees racing to grab the last seat; seats reduced by the owner after bookings existed; trying to reserve a slot whose booking is at capacity.

Related errors


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