{"record":{"id":"22d7a6724acbf23d","repo":"instructure/canvas-lms","slug":"course-required","errorCode":null,"errorMessage":"Course required","messagePattern":"Course required","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/models/course_section.rb","lineNumber":244,"sourceCode":"    return true if !root_account_id_changed? && !integration_id_changed?\n\n    scope = root_account.course_sections.where(integration_id:)\n    scope = scope.where(\"id<>?\", self) unless new_record?\n\n    return true unless scope.exists?\n\n    errors.add(:integration_id, t(\"integration_id_taken\", \"INTEGRATION ID \\\"%{integration_id}\\\" is already in use\", integration_id:))\n    throw :abort\n  end\n\n  alias_method :parent_event_context, :course\n\n  def section_code\n    name\n  end\n\n  def infer_defaults\n    raise \"Course required\" unless course\n\n    self.root_account_id = course.root_account_id || Account.default.id\n    # This is messy, and I hate it.\n    # The SIS import actually gives us three names for a section\n    #   and I don't know which one is best, or which one to show.\n    # Here's the current plan:\n    # - otherwise, just use name\n    # - use the method display_name to consolidate this logic\n    self.name ||= course.name if default_section\n    self.name ||= \"#{course.name} #{Time.zone.today}\"\n  end\n\n  def defined_by_sis?\n    !!sis_source_id\n  end\n\n  # NOTE: Don't assume the section_name contains the course name\n  # it might include it if the SIS specifies, but you shouldn't","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/course_section.rb#L226-L262","documentation":"CourseSection#infer_defaults sets root_account_id and derives section names/sis defaults from the parent course; it requires the course association to be set and raises 'Course required' otherwise. Called from before_validation/before_save during section creation.","triggerScenarios":"Creating or saving a CourseSection without assigning section.course = course first; bulk-importing sections without linking them to a course; SIS import code paths that build sections detached from their course.","commonSituations":"Hand-constructed CourseSection.new(...) in scripts/console missing the course; order-of-operations bug where course is assigned after validation triggers; importers creating sections per row but forgetting the FK on malformed rows.","solutions":["Assign section.course = the Course (or course_id) before saving so infer_defaults has its parent.","Build sections via course.course_sections.create instead of standalone CourseSection.new.","Reorder code so the course association is set before any save/validation runs."],"exampleFix":"// before\nsection = CourseSection.new(name: 'Section A')\nsection.save!\n\n// after\nsection = course.course_sections.new(name: 'Section A')\nsection.save!","handlingStrategy":"type-guard","validationCode":"raise 'course missing' unless section.course || section.course_id","typeGuard":"def section_has_course?(section)\n  section.course.present? || section.course_id.present?\nend","tryCatchPattern":null,"preventionTips":["Always build sections via course.course_sections.new/create","Validate imported rows have a resolvable course before instantiation","Assign course before any save triggers callbacks"],"tags":["ruby-on-rails","course-sections","missing-association"],"backgroundTag":"missing-required-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"}