{"record":{"id":"39a6f6ca983c536b","repo":"instructure/canvas-lms","slug":"must-provide-a-valid-teacher","errorCode":null,"errorMessage":"Must provide a valid teacher","messagePattern":"Must provide a valid teacher","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/models/course.rb","lineNumber":1994,"sourceCode":"      SisBatchRollBackData.bulk_insert_roll_back_data(data) if data\n      Enrollment.where(id: e_batch.map(&:id)).update_all(workflow_state: \"deleted\", updated_at: Time.zone.now, archived_at:)\n      EnrollmentState.where(enrollment_id: e_batch.map(&:id))\n                     .update_all([\"state = ?, state_is_current = ?, lock_version = lock_version + 1, updated_at = ?\", \"deleted\", true, Time.now.utc])\n      User.touch_and_clear_cache_keys(user_ids, :enrollments)\n      User.delay_if_production.update_account_associations(user_ids) if user_ids.any?\n    end\n    c_data = SisBatchRollBackData.build_dependent_data(sis_batch:, contexts: courses, updated_state: \"deleted\", batch_mode_delete: batch_mode)\n    SisBatchRollBackData.bulk_insert_roll_back_data(c_data) if c_data\n    Course.where(id: courses).update_all(workflow_state: \"deleted\", updated_at: Time.zone.now, archived_at:)\n    courses.count\n  end\n\n  def call_event(event)\n    send(event) if current_state.events.include? event.to_sym\n  end\n\n  def claim_with_teacher(user)\n    raise \"Must provide a valid teacher\" unless user\n    return unless state == :created\n\n    e = enroll_user(user, \"TeacherEnrollment\", enrollment_state: \"active\") # teacher(user)\n    claim\n    e\n  end\n\n  def self.require_assignment_groups(contexts)\n    courses = contexts.grep(Course)\n    groups = Shard.partition_by_shard(courses) do |shard_courses|\n      AssignmentGroup.select(\"id, context_id, context_type\").where(context_type: \"Course\", context_id: shard_courses)\n    end.index_by(&:context_id)\n    courses.each do |course|\n      unless groups[course.id]\n        course.require_assignment_group\n      end\n    end\n  end","sourceCodeStart":1976,"sourceCodeEnd":2012,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/course.rb#L1976-L2012","documentation":"Guard in Course (teacher enrollment path, course.rb ~1994): when adding a teacher to a course, the supplied user either isn't found or isn't enrollable as a teacher, so 'Must provide a valid teacher' is raised instead of creating an invalid teacher enrollment.","triggerScenarios":"Calling course.claim_with_teacher(nil) or with an expression that evaluated to nil (e.g. User.find_by that found nothing) for a course whose state is :created.","commonSituations":"Console/admin scripts claiming soft-concluded or self-enrollment courses with a lookup that returned nil; API code passing an optional user param straight through.","solutions":["Resolve a real User first and check presence before calling claim_with_teacher","Fall back to the current user or an admin as the claiming teacher when appropriate","Note claim_with_teacher is a no-op (returns nil) if state != :created, so also verify state"],"exampleFix":"// before\ncourse.claim_with_teacher(User.find_by(email: email))\n// after\nteacher = User.find_by(email: email)\nraise 'teacher not found' unless teacher\ncourse.claim_with_teacher(teacher)","handlingStrategy":"validation","validationCode":"raise 'teacher required' unless teacher.is_a?(User)\nreturn unless course.state == :created\ncourse.claim_with_teacher(teacher)","typeGuard":"def valid_teacher(u)\n  u.is_a?(User) && !u.new_record?\nend","tryCatchPattern":"begin\n  course.claim_with_teacher(teacher)\nrescue RuntimeError => e\n  raise unless e.message == 'Must provide a valid teacher'\n  # resolve a fallback teacher or abort\nend","preventionTips":["Verify User lookup results before claiming courses","Check course workflow_state is :created (otherwise the call silently no-ops)","Pass explicit user objects, not optional params"],"tags":["ruby","rails","course","null-argument"],"backgroundTag":"null-argument","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"}