instructure/canvas-lms · error · ImportError

Section # references course # which doesn't exist

Error message

Section #{section_id} references course #{course_id} which doesn't exist

What it means

Referential-integrity error in SectionImporter::Work#add_section raised when the course_id supplied for the section passes the blank check but no matching course exists (or was not yet imported) in the root account. The section cannot be attached, so ingestion of the row fails.

Solutions

  1. Import/verify the course before its sections in the SIS run
  2. Confirm the course_id matches the course's sis_source_id
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at lib/sis/section_importer.rb:88 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/62228c436a8c6646. Report an issue: GitHub.

Appendix: source

Thrown at lib/sis/section_importer.rb:88

        @batch = batch
        @root_account = root_account
        @logger = logger
        @success_count = 0
        @sections_to_update_sis_batch_ids = []
        @roll_back_data = []
        @course_ids_to_update_associations = Set.new
        @deleted_section_ids = Set.new
      end

      def add_section(section_id, course_id, name, status, start_date = nil, end_date = nil, integration_id = nil)
        raise ImportError, "No section_id given for a section in course #{course_id}" if section_id.blank?
        raise ImportError, "No course_id given for a section #{section_id}" if course_id.blank?
        raise ImportError, "No name given for section #{section_id} in course #{course_id}" if name.blank? && status =~ /\Aactive/i
        raise ImportError, "Improper status \"#{status}\" for section #{section_id} in course #{course_id}" unless /\Aactive|\Adeleted/i.match?(status)
        return if @batch.skip_deletes? && status =~ /deleted/i

        course = @root_account.all_courses.find_by(sis_source_id: course_id)
        raise ImportError, "Section #{section_id} references course #{course_id} which doesn't exist" unless course

        section = @root_account.course_sections.find_by(sis_source_id: section_id)
        section ||= course.course_sections.where(sis_source_id: section_id).first_or_initialize
        section.root_account = @root_account
        # this is an easy way to load up the cache with data we already have
        section.course = course if course.id == section.course_id

        # only update the name on new records, and ones that haven't been changed since the last sis import
        raise ImportError, "No name given for section #{section_id} in course #{course_id}" if name.blank? && section.new_record?

        section.name = name if section.new_record? || (!section.stuck_sis_fields.include?(:name) && name.present?)

        # update the course id if necessary
        if section.course_id != course.id
          if section.nonxlist_course_id
            # this section is crosslisted
            if (section.nonxlist_course_id != course.id && !section.stuck_sis_fields.include?(:course_id)) || (section.course.workflow_state == "deleted" && status.start_with?("active"))
              # but the course id we were given didn't match the crosslist info

View on GitHub (pinned to 1c9f0bb801)