instructure/canvas-lms · error · CalendarEvent::ReservationError
ineligible participant
Error message
ineligible participant
What it means
Raised by CalendarEvent#reserve_for when the appointment slot's context reports that the participant is not eligible (AppointmentGroup#eligible_participant?). Even on a valid appointment slot, groups/users outside the appointment group's subcontext (e.g. a specific course section) or not meeting participant-type rules cannot book it.
Solutions
- Check eligibility first: appointment_group.eligible_participant?(participant) and only reserve when true.
- Pass the correct participant type — for group-signup appointment groups pass the Group, not the User.
- Verify the participant is enrolled in the appointment group's subcontext (course/section) and re-sync enrollments if stale.
- Update the appointment group's subcontext/participant visibility if the intended audience should be allowed to book.
Example fix
# before
slot.reserve_for(current_user, current_user)
# after (group-appointment case)
participant = appointment_group.participant_type == "Group" ? current_user.groups.find { |g| appointment_group.eligible_participant?(g) } : current_user
slot.reserve_for(participant, current_user) if appointment_group.eligible_participant?(participant) Defensive patterns
Strategy: validation
Validate before calling
appointment_group = slot.context raise ReservationError, "ineligible participant" unless appointment_group.eligible_participant?(participant)
Try / catch
begin
slot.reserve_for(participant, user)
rescue CalendarEvent::ReservationError => e
render json: { error: e.message }, status: :forbidden
end Prevention
- Call eligible_participant? before reserving
- Pass a Group for group-signup appointment groups, a User otherwise
- Keep enrollment data in sync so subcontext membership is current
- Communicate slot-visibility rules to API consumers
When it happens
Trigger: reserve_for(participant, user) is called with a participant (user or group) that is not part of the appointment group's subcontext — different course/section, participant category mismatch (user vs group appointments), or the participant was removed from the course after the appointment group was created.
Common situations: Student tries to book a slot scoped to another course section; group-appointment slot booked directly by a user instead of the group; participant dropped the course; API client passes the current user as participant when the appointment group is limited to group categories.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Failed updating an event in the series, update not saved
- invalid participant
- not an appointment
- all slots filled
- cannot cancel past reservation
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/088861abae0f76dc.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/calendar_event.rb:593
end
def appointment_group
if parent_event.try(:context).is_a?(AppointmentGroup)
parent_event.context
elsif context_type == "AppointmentGroup"
context
end
end
def account
(context_type == "Account") ? context : nil
end
class ReservationError < StandardError; end
def reserve_for(participant, user, options = {})
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?
View on GitHub (pinned to 1c9f0bb801)