instructure/canvas-lms · error · CalendarEvent::ReservationError
participant has already reserved this appointment
Error message
participant has already reserved this appointment
What it means
CalendarEvent.reserve_for raises ReservationError when the participant already holds a reservation (child_events_for(participant) is non-empty) on this specific appointment slot. A participant can only reserve a given slot once.
Solutions
- Treat this as success/idempotent: check child_events_for(participant) first and no-op if a reservation already exists.
- Disable the booking button / debounce submissions in the client before re-posting.
- If a different reservation is intended, cancel the existing one first, then call reserve_for with cancel_existing: true (only if it is in the future).
Example fix
// before slot.reserve_for(user, participant) // after unless slot.child_events_for(participant).present? slot.reserve_for(user, participant) end
Defensive patterns
Strategy: validation
Validate before calling
return if slot.child_events_for(participant).present? # already reserved, treat as idempotent success
Try / catch
begin
slot.reserve_for(user, participant)
rescue ReservationError => e
raise unless e.message.include?('already reserved')
# treat as success
end Prevention
- Check child_events_for(participant) before booking
- Debounce/disable submit buttons to prevent double posts
- Make API clients idempotent: query before create
When it happens
Trigger: Calling reserve_for twice for the same participant on the same slot; double-click on the booking button issuing two requests; a retry after a timeout when the first request actually succeeded.
Common situations: Duplicate form submissions; idempotency-missing API clients; users refreshing a confirmation page that re-posts the booking.
Related errors
- all slots filled
- cannot cancel past reservation
- ineligible participant
- invalid participant
- not an appointment
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/65d3e9eee6d85c13.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/calendar_event.rb:610
raise ReservationError, "not an appointment" unless context_type == "AppointmentGroup"
raise ReservationError, "ineligible participant" unless context.eligible_participant?(participant)
transaction do
lock! # in case two people two participants try to grab the same slot
participant.lock! # in case two people try to make a reservation for the same participant
if options[:cancel_existing]
context.reservations_for(participant).lock.each do |reservation|
raise ReservationError, "cannot cancel past reservation" if reservation.end_at < Time.now.utc
reservation.updating_user = user
reservation.destroy
end
end
raise ReservationError, "participant has met per-participant limit" if context.max_appointments_per_participant && context.reservations_for(participant).size >= context.max_appointments_per_participant
raise ReservationError, "all slots filled" if participants_per_appointment && child_events.size >= participants_per_appointment
raise ReservationError, "participant has already reserved this appointment" if child_events_for(participant).present?
event = child_events.build
event.updating_user = user
event.context = participant
event.workflow_state = :locked
event.comments = options[:comments]
event.save!
if active?
self.workflow_state = "locked"
save!
end
context.clear_cached_available_slots!
event
end
end
def child_events_for(participant)
if child_events.loaded?View on GitHub (pinned to 1c9f0bb801)