{"record":{"id":"baf72069e22d7aef","repo":"calcom/cal.diy","slug":"attendee-with-this-email-can-t-book-because-the-ma","errorCode":null,"errorMessage":"Attendee with this email can't book because the maximum number of active bookings has been reached.","messagePattern":"Attendee with this email can't book because the maximum number of active bookings has been reached\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"apps/api/v2/src/platform/bookings/2024-08-13/services/errors.service.ts","lineNumber":52,"sourceCode":"  handleBookingError(error: unknown, bookingTeamEventType: boolean): never {\n    const hostsUnavaile = \"One of the hosts either already has booking at this time or is not available\";\n\n    if (error instanceof Error) {\n      if (error.message === \"no_available_users_found_error\") {\n        if (bookingTeamEventType) {\n          throw new BadRequestException(hostsUnavaile);\n        }\n        throw new BadRequestException(\"User either already has booking at this time or is not available\");\n      } else if (error.message === \"booking_time_out_of_bounds_error\") {\n        throw new BadRequestException(\n          `The event type can't be booked at the \"start\" time provided. This could be because it's too soon (violating the minimum booking notice) or too far in the future (outside the event's scheduling window). Try fetching available slots first using the GET /v2/slots endpoint and then make a booking with \"start\" time equal to one of the available slots.`\n        );\n      } else if (error.message === \"Attempting to book a meeting in the past.\") {\n        throw new BadRequestException(\"Attempting to book a meeting in the past.\");\n      } else if (error.message === \"hosts_unavailable_for_booking\") {\n        throw new BadRequestException(hostsUnavaile);\n      } else if (error.message === \"booker_limit_exceeded_error\") {\n        throw new BadRequestException(\n          \"Attendee with this email can't book because the maximum number of active bookings has been reached.\"\n        );\n      } else if (error.message === \"booker_limit_exceeded_error_reschedule\") {\n        const errorData =\n          \"data\" in error ? (error.data as { rescheduleUid: string }) : { rescheduleUid: undefined };\n        let message =\n          \"Attendee with this email can't book because the maximum number of active bookings has been reached.\";\n        if (errorData?.rescheduleUid) {\n          message += ` You can reschedule your existing booking (${errorData.rescheduleUid}) to a new timeslot instead.`;\n        }\n        throw new BadRequestException(message);\n      }\n    }\n    throw error;\n  }\n}\n","sourceCodeStart":34,"sourceCodeEnd":69,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/bookings/2024-08-13/services/errors.service.ts#L34-L69","documentation":"Thrown by handleBookingError when error.message === 'booker_limit_exceeded_error'. BadRequestException (HTTP 400). The attendee's email has reached the event type's per-attendee active-booking limit, so a new booking is refused. No reschedule context is attached in this branch (that is error 332).","triggerScenarios":"Booking an event type that enforces a maximum number of active bookings per attendee email (bookerLimit), when the attendee already holds that many active (non-cancelled) bookings. Typical for classes, seated events, or limited-trial event types.","commonSituations":"Attendee tries to double-book a limited class; previous bookings were not cancelled before booking a new slot; bookerLimit was lowered after earlier bookings were made; attendee email reused across test accounts hitting the same limit.","solutions":["Cancel one of the attendee's existing active bookings before creating a new one.","Raise the event type's bookerLimit if concurrent bookings should be allowed.","Check the attendee's active booking count via GET /v2/bookings?attendeeEmail=... before attempting to book.","Offer a reschedule of an existing booking instead (which routes through the 332 branch)."],"exampleFix":"// before\nawait api.post('/v2/bookings', { ... attendee: { email: 'a@b.com' } });\n\n// after\nconst active = await api.get(`/v2/bookings?attendeeEmail=a@b.com&status=upcoming`);\nif (active.data.length >= bookerLimit) {\n  await api.delete(`/v2/bookings/${active.data[0].uid}`);\n}\nawait api.post('/v2/bookings', { ... attendee: { email: 'a@b.com' } });","handlingStrategy":"validation","validationCode":"const active = await api.get(`/v2/bookings?attendeeEmail=${encodeURIComponent(email)}&status=upcoming`);\nif (active.data.length >= bookerLimit) throw new Error('booker limit reached — cancel an existing booking first');","typeGuard":"null","tryCatchPattern":"try { await api.post('/v2/bookings', body); }\ncatch (e) {\n  if (e.response?.status === 400 && /maximum number of active bookings/.test(e.response.data.message)) {\n    /* offer cancel-or-reschedule flow */\n  } else throw e;\n}","preventionTips":["Check the attendee's active booking count before creating a new one.","Cancel or reschedule existing bookings before re-booking.","Surface the bookerLimit in the booking UI."],"tags":["api","bookings","booker-limit","validation","nestjs"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}