{"record":{"id":"a18c0a537d5cce36","repo":"calcom/cal.diy","slug":"booking-with-id-input-eventtypeid-at-input-sl","errorCode":null,"errorMessage":"Booking with id=${input.eventTypeId} at ${input.slotStart} has no more seats left.","messagePattern":"Booking with id=(.+?) at (.+?) has no more seats left\\.","errorType":"http","errorClass":"UnprocessableEntityException","httpStatus":422,"severity":"error","filePath":"apps/api/v2/src/modules/slots/slots-2024-09-04/services/slots.service.ts","lineNumber":144,"sourceCode":"    }\n\n    const endDate = startDate.plus({ minutes: input.slotDuration ?? eventType.length });\n    if (!endDate.isValid) {\n      throw new BadRequestException(\"Invalid end date\");\n    }\n\n    const booking = await this.slotsRepository.findActiveOverlappingBooking(\n      input.eventTypeId,\n      startDate.toJSDate(),\n      endDate.toJSDate()\n    );\n\n    if (eventType.seatsPerTimeSlot) {\n      const attendeesCount = booking?.attendees?.length;\n      if (attendeesCount) {\n        const seatsLeft = eventType.seatsPerTimeSlot - attendeesCount;\n        if (seatsLeft < 1) {\n          throw new UnprocessableEntityException(\n            `Booking with id=${input.eventTypeId} at ${input.slotStart} has no more seats left.`\n          );\n        }\n      }\n    }\n\n    const nonSeatedEventAlreadyBooked = !eventType.seatsPerTimeSlot && booking;\n    const isRoundRobinEvent = eventType.schedulingType === SchedulingType.ROUND_ROBIN;\n\n    if (nonSeatedEventAlreadyBooked && !isRoundRobinEvent) {\n      throw new UnprocessableEntityException(`Can't reserve a slot if the event is already booked.`);\n    }\n\n    if (isRoundRobinEvent) {\n      try {\n        await validateRoundRobinSlotAvailability(input.eventTypeId, startDate, endDate, eventType.hosts);\n      } catch (error) {\n        if (error instanceof Error) {","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/slots/slots-2024-09-04/services/slots.service.ts#L126-L162","documentation":"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.","triggerScenarios":"POST /v2/slots/reserve for a seated event type where the existing booking at that time already has attendees equal to or exceeding seatsPerTimeSlot.","commonSituations":"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.","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."],"exampleFix":"// before\nfetch('/v2/slots/reserve', { body: JSON.stringify({ eventTypeId: seatedEtId, slotStart }) });\n\n// after — pick an open seat from availability first\nconst slots = await calApi.slots.list({ eventTypeId: seatedEtId, ... });\nconst open = slots.find(s => s.seatsRemaining && s.seatsRemaining > 0);\nif (!open) throw new ClientError('No seats available');\nfetch('/v2/slots/reserve', { body: JSON.stringify({ eventTypeId: seatedEtId, slotStart: open.start }) });","handlingStrategy":"try-catch","validationCode":"async function findOpenSeatedSlot(eventTypeId: number, window: { start: string; end: string }) {\n  const slots = await cal.slots.list({ eventTypeId, ...window });\n  const open = Object.values(slots).flat().find(s => typeof s.seatsRemaining === 'number' && s.seatsRemaining > 0);\n  if (!open) throw new UserFacingError('All seats are booked — pick another time.');\n  return open;\n}\nconst open = await findOpenSeatedSlot(eventTypeId, { start, end });","typeGuard":"function hasOpenSeats(slot: unknown): slot is { start: string; seatsRemaining: number } {\n  return typeof slot === 'object' && slot !== null\n    && typeof (slot as any).start === 'string'\n    && typeof (slot as any).seatsRemaining === 'number'\n    && (slot as any).seatsRemaining > 0;\n}","tryCatchPattern":"try {\n  await cal.slots.reserve({ eventTypeId, slotStart });\n} catch (e) {\n  if (e instanceof HttpError && e.statusCode === 422 && /no more seats/i.test(e.message)) {\n    const next = await findOpenSeatedSlot(eventTypeId, window);\n    return cal.slots.reserve({ eventTypeId, slotStart: next.start });\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["calcom-api","slots","unprocessable","seated-event","capacity","reservation"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}