instructure/canvas-lms · error · GraphQL::ExecutionError
Enrollment is not in invited state
Error message
Enrollment is not in invited state
What it means
Raised when the enrollment exists and belongs to the user but its workflow_state is not 'invited' (enrollment.invited? is false). Only pending invitations can be rejected; already-accepted, completed, rejected, or deleted enrollments are invalid targets.
Solutions
- Check enrollment.workflow_state == 'invited' before calling; surface a friendly message otherwise
- Disable the reject action in the UI after the first successful call
- If already accepted, instruct the user to leave the course instead of rejecting
- Handle idempotency: treat 'already rejected' outcomes as success in client logic
Example fix
// before
// second click after rejection
rejectEnrollmentInvitation(uuid)
// after
if (enrollment.workflowState === 'invited') {
await rejectEnrollmentInvitation(uuid)
} else {
showMessage('This invitation has already been processed.')
} Defensive patterns
Strategy: validation
Validate before calling
if (enrollment.workflowState !== 'invited') {
show('This invitation has already been ' + enrollment.workflowState)
return
} Type guard
function isInvitable(e) { return e?.workflowState === 'invited' } Try / catch
try {
await rejectEnrollmentInvitation({ enrollmentUuid })
} catch (e) {
if (e.message.includes('not in invited state')) { refetchEnrollment(); show('Invitation already processed') }
else throw e
} Prevention
- Disable the reject button immediately after a successful call
- Check workflow_state === 'invited' before rendering the action
- Handle stale email links gracefully with state re-fetch
- Avoid concurrent mutations from multiple tabs
When it happens
Trigger: Double-clicking the reject action so the second call hits an already-rejected enrollment; rejecting after the user already accepted the course; enrollment auto-accepted due to a prior interaction; enrollment hard/soft deleted by an admin in the meantime.
Common situations: Users clicking a stale 'decline invitation' email link after previously accepting; UI not refreshing state after the first rejection; race between two browser tabs.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Enrollment invitation not found
- Enrollment is not in invited state
- Must be logged in
- Unauthorized
- and cannot be used together
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/10c34a342849e790.
Report an issue: GitHub.
Appendix: source
Thrown at app/graphql/mutations/reject_enrollment_invitation.rb:39
module Mutations
class RejectEnrollmentInvitation < BaseMutation
argument :enrollment_uuid, String, required: true
field :enrollment, Types::EnrollmentType, null: true
field :success, Boolean, null: false
def resolve(input:, **)
user = context[:current_user]
raise GraphQL::ExecutionError, I18n.t("Must be logged in") unless user
enrollment = Enrollment.where(uuid: input[:enrollment_uuid]).first
raise GraphQL::ExecutionError, I18n.t("Enrollment invitation not found") unless enrollment
# Verify the enrollment belongs to the current user
raise GraphQL::ExecutionError, I18n.t("Unauthorized") unless enrollment.user == user
# Verify the enrollment is in invited state
raise GraphQL::ExecutionError, I18n.t("Enrollment is not in invited state") unless enrollment.invited?
begin
if enrollment.reject
{
enrollment:,
success: true
}
else
{
enrollment: nil,
success: false,
errors: [{ message: I18n.t("Failed to reject enrollment invitation") }]
}
end
rescue => e
{
enrollment: nil,
success: false,View on GitHub (pinned to 1c9f0bb801)