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
- Re-query GET /v2/slots for the seated event and pick a slot with seatsRemaining > 0.
- Surface a 'fully booked' message and offer alternative times.
- Retry on a different slot rather than the same slotStart.
- 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
- Filter slot lists to seatsRemaining > 0 before offering them.
- Surface 'fully booked' and offer alternatives rather than retrying the same slot.
- Refresh availability right before reserve in high-demand flows.
- Increase seatsPerTimeSlot if the owner wants more capacity.
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
- Can't reserve a slot if the event is already booked.
- This time slot is already reserved by another user. Please c
- reservationDuration can only be used for authenticated reque
- Event Type with ID=${input.eventTypeId} not found
- authenticated user is not owner of event type, does not have
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/a18c0a537d5cce36.
Report an issue: GitHub.