{"record":{"id":"5be538f02d64f036","repo":"instructure/canvas-lms","slug":"cannot-call-enrollment-state-on-a-new-record","errorCode":null,"errorMessage":"cannot call enrollment_state on a new record","messagePattern":"cannot call enrollment_state on a new record","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/models/enrollment.rb","lineNumber":842,"sourceCode":"    end\n\n    state :deleted\n    state :rejected do\n      event :unreject, transitions_to: :invited\n    end\n    state :completed\n\n    # Inactive is a \"hard\" state, i.e. tuition not paid\n    state :inactive\n  end\n\n  def enrollment_dates\n    Canvas::Builders::EnrollmentDateBuilder.preload([self]) unless @enrollment_dates\n    @enrollment_dates\n  end\n\n  def enrollment_state\n    raise \"cannot call enrollment_state on a new record\" if new_record?\n\n    result = super\n    unless result\n      association(:enrollment_state).reload\n      result = super\n    end\n    # ensure we have an enrollment state object present with a reverse association\n    result ||= create_enrollment_state\n    result.enrollment = self\n    result\n  end\n\n  def create_enrollment_state\n    self.enrollment_state =\n      shard.activate do\n        GuardRail.activate(:primary) do\n          EnrollmentState.unique_constraint_retry do\n            EnrollmentState.where(enrollment_id: self).first_or_create","sourceCodeStart":824,"sourceCodeEnd":860,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/enrollment.rb#L824-L860","documentation":"Enrollment#enrollment_state overrides the enrollment_state association reader and refuses to run on unsaved records, because the EnrollmentState row is derived from persisted enrollment data; reading it for a new_record? would be meaningless. create_enrollment_state and other code must therefore only call this on persisted enrollments.","triggerScenarios":"Calling enrollment.enrollment_state on a build/new Enrollment that has not been saved — e.g. computing state before create, or create_enrollment_state invoked on an unsaved enrollment in a callback-order bug.","commonSituations":"Specs calling enrollment_state on Enrollment.new; after_build-style code touching enrollment_state before save; code paths that construct transient enrollments for previews or invitations.","solutions":["Save the enrollment before reading enrollment_state","Guard with return if enrollment.new_record? before accessing enrollment_state","Use enrollment_state_overrides/computed state helpers that work on unsaved records if you need predicted state","In specs, create! the enrollment instead of building it"],"exampleFix":"// before\nstate = enrollment.enrollment_state # raises for new record\n// after\nstate = enrollment.new_record? ? nil : enrollment.enrollment_state","handlingStrategy":"type-guard","validationCode":"next if enrollment.new_record?\nstate = enrollment.enrollment_state","typeGuard":"def persisted_enrollment?(enrollment)\n  enrollment.is_a?(Enrollment) && !enrollment.new_record?\nend","tryCatchPattern":"begin\n  state = enrollment.enrollment_state\nrescue RuntimeError => e\n  raise unless e.message =~ /new record/\n  state = nil\nend","preventionTips":["Never read association-backed state on unsaved records","create! enrollments in specs rather than building","Ensure callbacks that touch enrollment_state run post-persist"],"tags":["ruby","rails","enrollment","new-record","persistence"],"backgroundTag":"invalid-argument-value","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"}