{"record":{"id":"2969ce8bc6462785","repo":"instructure/canvas-lms","slug":"enrollment-invitation-not-found-reject","errorCode":null,"errorMessage":"Enrollment invitation not found","messagePattern":"Enrollment invitation not found","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/reject_enrollment_invitation.rb","lineNumber":33,"sourceCode":"# details.\n#\n# You should have received a copy of the GNU Affero General Public License along\n# with this program. If not, see <http://www.gnu.org/licenses/>.\n#\n\nmodule Mutations\n  class RejectEnrollmentInvitation < BaseMutation\n    argument :enrollment_uuid, String, required: true\n\n    field :enrollment, Types::EnrollmentType, null: true\n    field :success, Boolean, null: false\n\n    def resolve(input:, **)\n      user = context[:current_user]\n      raise GraphQL::ExecutionError, I18n.t(\"Must be logged in\") unless user\n\n      enrollment = Enrollment.where(uuid: input[:enrollment_uuid]).first\n      raise GraphQL::ExecutionError, I18n.t(\"Enrollment invitation not found\") unless enrollment\n\n      # Verify the enrollment belongs to the current user\n      raise GraphQL::ExecutionError, I18n.t(\"Unauthorized\") unless enrollment.user == user\n\n      # Verify the enrollment is in invited state\n      raise GraphQL::ExecutionError, I18n.t(\"Enrollment is not in invited state\") unless enrollment.invited?\n\n      begin\n        if enrollment.reject\n          {\n            enrollment:,\n            success: true\n          }\n        else\n          {\n            enrollment: nil,\n            success: false,\n            errors: [{ message: I18n.t(\"Failed to reject enrollment invitation\") }]","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/reject_enrollment_invitation.rb#L15-L51","documentation":"Raised when no Enrollment record matches the supplied enrollment_uuid. The mutation looks up Enrollment.where(uuid: input[:enrollment_uuid]).first and raises this error when the lookup returns nil, so the invitation cannot be rejected.","triggerScenarios":"Passing a UUID that is not an enrollment uuid (e.g. the course uuid or a user uuid); the enrollment was already accepted/rejected/deleted and purged; typo or truncated uuid; querying on the wrong shard/root account.","commonSituations":"Stale email links after the enrollment was re-created (regenerated uuid); user clicks 'decline' twice; uuid copied from a different environment (test vs production).","solutions":["Verify the uuid corresponds to an existing Enrollment via Enrollment.where(uuid: ...).exists?","Re-send/refresh the invitation link to get the current enrollment uuid","Handle the already-processed case in the UI instead of re-invoking the mutation","Confirm you are hitting the correct Canvas environment/shard"],"exampleFix":"// before\nrejectEnrollmentInvitation(courseUuid)\n// after\nrejectEnrollmentInvitation(enrollmentUuid) // Enrollment.find_by(uuid: enrollmentUuid)&.uuid","handlingStrategy":"validation","validationCode":"const enrollment = await fetchEnrollmentByUuid(uuid)\nif (!enrollment) show('Invitation no longer valid')","typeGuard":"function isEnrollment(e) { return e != null && typeof e.uuid === 'string' && e.workflowState != null }","tryCatchPattern":"try {\n  await rejectEnrollmentInvitation({ enrollmentUuid })\n} catch (e) {\n  if (e.message.includes('Enrollment invitation not found')) {\n    show('This invitation has expired or was already processed'); refetchState()\n  } else throw e\n}","preventionTips":["Pass the enrollment uuid, not course/user uuid","Re-fetch the invitation link server-side before rendering the decline action","Treat 'not found' as already-processed in idempotent UI flows","Confirm you are in the correct environment/shard"],"tags":["graphql","enrollments","not-found"],"backgroundTag":"record-not-found","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"}