{"record":{"id":"0462ad6d08da40e7","repo":"instructure/canvas-lms","slug":"all-slots-filled","errorCode":null,"errorMessage":"all slots filled","messagePattern":"all slots filled","errorType":"exception","errorClass":"CalendarEvent::ReservationError","httpStatus":null,"severity":"error","filePath":"app/models/calendar_event.rb","lineNumber":609,"sourceCode":"  def reserve_for(participant, user, options = {})\n    raise ReservationError, \"not an appointment\" unless context_type == \"AppointmentGroup\"\n    raise ReservationError, \"ineligible participant\" unless context.eligible_participant?(participant)\n\n    transaction do\n      lock! # in case two people two participants try to grab the same slot\n      participant.lock! # in case two people try to make a reservation for the same participant\n\n      if options[:cancel_existing]\n        context.reservations_for(participant).lock.each do |reservation|\n          raise ReservationError, \"cannot cancel past reservation\" if reservation.end_at < Time.now.utc\n\n          reservation.updating_user = user\n          reservation.destroy\n        end\n      end\n\n      raise ReservationError, \"participant has met per-participant limit\" if context.max_appointments_per_participant && context.reservations_for(participant).size >= context.max_appointments_per_participant\n      raise ReservationError, \"all slots filled\" if participants_per_appointment && child_events.size >= participants_per_appointment\n      raise ReservationError, \"participant has already reserved this appointment\" if child_events_for(participant).present?\n\n      event = child_events.build\n      event.updating_user = user\n      event.context = participant\n      event.workflow_state = :locked\n      event.comments = options[:comments]\n      event.save!\n      if active?\n        self.workflow_state = \"locked\"\n        save!\n      end\n      context.clear_cached_available_slots!\n      event\n    end\n  end\n\n  def child_events_for(participant)","sourceCodeStart":591,"sourceCodeEnd":627,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/calendar_event.rb#L591-L627","documentation":"CalendarEvent.reserve_for raises ReservationError when the slot's participants_per_appointment is set and the number of child_events (existing reservations on this slot) already meets that capacity. The slot is full so no additional participant can be added.","triggerScenarios":"Calling reserve_for on a slot where participants_per_appointment is non-nil and child_events.size >= participants_per_appointment.","commonSituations":"Two students racing to grab the last spot in a group slot; API scripts retrying a booking that already succeeded; UI showing a stale open slot that another user just filled.","solutions":["Show a 'slot is full' message and offer other open slots in the same appointment group.","Re-fetch slot availability immediately before booking to reduce the race window (the lock! calls already serialize server-side).","Increase participants_per_appointment on the appointment group if group bookings genuinely need more seats."],"exampleFix":"// before\nslot.reserve_for(user, participant)\n// after\nif slot.participants_per_appointment && slot.child_events.size >= slot.participants_per_appointment\n  render json: { error: 'This time slot is full' }, status: :conflict\nelse\n  slot.reserve_for(user, participant)\nend","handlingStrategy":"try-catch","validationCode":"if slot.participants_per_appointment && slot.child_events.size >= slot.participants_per_appointment\n  raise UserFacingError, 'slot full'\nend","typeGuard":null,"tryCatchPattern":"begin\n  slot.reserve_for(user, participant)\nrescue ReservationError => e\n  if e.message == 'all slots filled'\n    render_slot_full_response\n  else\n    raise\n  end\nend","preventionTips":["Re-fetch child_events count immediately before booking","Surface slot capacity in the UI and disable full slots","Handle the conflict gracefully since concurrent bookings can always race"],"tags":["calendar","appointments","capacity","race-condition"],"backgroundTag":"capacity-exceeded","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}