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

  1. Check eligibility first: appointment_group.eligible_participant?(participant) and only reserve when true.
  2. Pass the correct participant type — for group-signup appointment groups pass the Group, not the User.
  3. Verify the participant is enrolled in the appointment group's subcontext (course/section) and re-sync enrollments if stale.
  4. 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

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


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)