{"record":{"id":"e15901a8750b0301","repo":"calcom/cal.diy","slug":"error-message-e15901","errorCode":null,"errorMessage":"${error?.message}","messagePattern":"\\$\\{error\\?\\.message\\}","errorType":"http","errorClass":"UnprocessableEntityException","httpStatus":422,"severity":"warning","filePath":"apps/api/v2/src/modules/slots/slots-2024-09-04/services/slots.service.ts","lineNumber":163,"sourceCode":"            `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) {\n          throw new UnprocessableEntityException(error?.message);\n        }\n        throw error;\n      }\n    } else {\n      await this.checkSlotOverlap(input.eventTypeId, startDate.toISO(), endDate.toISO());\n    }\n\n    const reservationDuration = input.reservationDuration ?? DEFAULT_RESERVATION_DURATION;\n\n    if (eventType.userId) {\n      const slot = await this.slotsRepository.createSlot(\n        eventType.userId,\n        eventType.id,\n        startDate.toISO(),\n        endDate.toISO(),\n        eventType.seatsPerTimeSlot !== null,\n        reservationDuration\n      );","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/slots/slots-2024-09-04/services/slots.service.ts#L145-L181","documentation":"A NestJS UnprocessableEntityException (HTTP 422) from SlotsService_2024_09_04.reserveSlot's round-robin branch. validateRoundRobinSlotAvailability threw an Error and its message is forwarded verbatim. NOTE: in this repository validateRoundRobinSlotAvailability is a stub (always returns true), so this error is effectively unreachable here; in the EE build it fires when no round-robin host is free for the slot.","triggerScenarios":"POST /v2/slots/reserve for a round-robin team event type where, in the EE validation, none of the hosts are available for the computed window. In this fork the stub never throws, so you will not see this in practice.","commonSituations":"Running against an EE build where host availability/busy times exclude everyone for that slot; hosts removed from the team after the slot list was generated.","solutions":["Re-query availability to find a slot where at least one round-robin host is free.","Add more hosts to the round-robin event type.","Widen the host pool (routedTeamMemberIds) if using routing.","If you observe this in the OSS fork, treat it as unexpected and report it — the stub should not throw."],"exampleFix":"// before\nawait reserve(rrEventTypeId, slotStart);\n\n// after — pick a slot confirmed to have an available RR host\nconst slots = await calApi.slots.list({ eventTypeId: rrEventTypeId });\nconst open = slots.find(s => !s.away);\nawait reserve(rrEventTypeId, open.start);","handlingStrategy":"try-catch","validationCode":"// Validate availability before reserving a round-robin slot.\nasync function ensureRrSlotHasHost(eventTypeId: number, slotStart: string) {\n  const slots = await cal.slots.list({ eventTypeId, start: slotStart, end: oneDayLater(slotStart) });\n  const hit = Object.values(slots).flat().find(s => s.start === slotStart && !s.away);\n  if (!hit) throw new UserFacingError('No round-robin host is free at that time.');\n  return hit;\n}\nawait ensureRrSlotHasHost(eventTypeId, slotStart);","typeGuard":"function isRoundRobinHostAvailable(slot: unknown): boolean {\n  return typeof slot === 'object' && slot !== null && !(slot as any).away;\n}","tryCatchPattern":"try {\n  await cal.slots.reserve({ eventTypeId, slotStart });\n} catch (e) {\n  if (e instanceof HttpError && e.statusCode === 422) {\n    // round-robbin: pick the next free slot\n    const next = await nextFreeSlot(eventTypeId);\n    return cal.slots.reserve({ eventTypeId, slotStart: next.start });\n  }\n  throw e;\n}","preventionTips":["Refresh availability before reserving round-robin slots.","Add more hosts to the event type to widen coverage.","Use routedTeamMemberIds to expand the host pool when routing.","In the OSS fork, treat this error as unexpected since the validator is a stub."],"tags":["calcom-api","slots","unprocessable","round-robin","host-availability","ee-only"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}