calcom/cal.diy · error · UnprocessableEntityException

This time slot is already reserved by another user. Please c

Error message

This time slot is already reserved by another user. Please choose a different time.

What it means

A NestJS UnprocessableEntityException (HTTP 422) from SlotsService_2024_09_04.checkSlotOverlap. For non-round-robin events, the slotsRepository.getOverlappingSlotReservation returned an existing reservation for the same window. Another user is already holding the slot within the reservation window; only one active reservation is allowed.

Source

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

      eventType.id,
      startDate.toISO(),
      endDate.toISO(),
      eventType.seatsPerTimeSlot !== null,
      reservationDuration
    );

    return this.slotsOutputService.getReservationSlotCreated(slot, reservationDuration);
  }

  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(
          ", "
        )}`

View on GitHub (pinned to 176037d0af)

Solutions

  1. Offer the user the next available slot from a freshly-fetched availability list.
  2. Wait for the existing reservation to expire (releaseAt) and retry if appropriate.
  3. Keep reservationDuration short to reduce overlap windows.
  4. Surface a 'slot just taken / held' message and refresh.

Example fix

// before
await reserve(eventTypeId, slotStart);

// after — refresh on 422 overlap and pick a different slot
try { await reserve(eventTypeId, slotStart); }
catch (e) {
  if (e.status === 422 && /already reserved/i.test(e.message)) {
    const next = await nextFreeSlot(eventTypeId);
    await reserve(eventTypeId, next.start);
  } else throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// You cannot fully prevent a race; minimize the window by keeping reservationDuration short
// and re-checking availability right before reserve.
async function ensureSlotNotReserved(eventTypeId: number, slotStart: string) {
  const slots = await cal.slots.list({ eventTypeId, start: slotStart, end: oneDayLater(slotStart) });
  const hit = Object.values(slots).flat().find(s => s.start === slotStart && !s.away);
  if (!hit) throw new UserFacingError('This time is being held by someone else.');
}
await ensureSlotNotReserved(eventTypeId, slotStart);

Type guard

function isReservableSlot(slot: unknown): slot is { start: string } {
  return typeof slot === 'object' && slot !== null
    && typeof (slot as any).start === 'string'
    && !(slot as any).away;
}

Try / catch

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

Prevention

When it happens

Trigger: POST /v2/slots/reserve where another reservation (a SelectedSlots row still within its releaseAt) overlaps the requested start/end. Common right after a popular slot opens or when two clients reserve concurrently.

Common situations: End user clicks the same time as another user; previous reservation not yet expired (default 5 min); retrying reserve after a network blip while the first reservation is still live; reservationDuration too long, blocking others.

Related errors


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