instructure/canvas-lms · error · ImportError

A cross-listing referenced a non-existent section #

Error message

A cross-listing referenced a non-existent section #{section_id}

What it means

The importer looks up the section by sis_source_id under the root account (CourseSections) and raises ImportError when no section matches. The cross-listing cannot proceed because the target section record does not exist in Canvas yet or its SIS ID differs.

Solutions

  1. Import the sections (sections.csv) before or in the same batch as crosslist.csv
  2. Verify the section_id matches the section's sis_source_id exactly (no typos, no stale IDs)
  3. Confirm the import is running against the correct root_account that contains the section
  4. Re-run the section import to recreate missing sections, then retry the crosslist import

Example fix

# before (ordering)
# crosslist.csv imported standalone
# after
# import sections.csv first, then crosslist.csv in the same sis batch
Defensive patterns

Strategy: validation

Validate before calling

section = root_account.course_sections.find_by(sis_source_id: section_id)
raise "Section #{section_id} not found; import sections first" unless section

Try / catch

begin
  xlist.add_crosslist(xlist_course_id, section_id, status)
rescue SIS::ImportError => e
  if e.message =~ /non-existent section/
    Rails.logger.error("Re-run sections.csv import; missing section #{section_id}")
  end
end

Prevention

When it happens

Trigger: add_crosslist is called with a section_id whose sis_source_id has no matching CourseSection in the root account — the section was never imported, was imported under a different SIS ID, or belongs to a different root account/subaccount.

Common situations: Crosslist.csv processed before sections.csv in the same SIS batch or a previous batch failed; section SIS IDs renamed upstream; importing into the wrong root account; sections deleted in Canvas but still present in the SIS export.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at lib/sis/xlist_importer.rb:59

      def initialize(batch, root_account, logger)
        @batch = batch
        @root_account = root_account
        @logger = logger
        @success_count = 0

        @course = nil
        @course_ids_to_update_associations = [].to_set
      end

      def add_crosslist(xlist_course_id, section_id, status)
        raise ImportError, "No xlist_course_id given for a cross-listing" if xlist_course_id.blank?
        raise ImportError, "No section_id given for a cross-listing" if section_id.blank?
        raise ImportError, "Improper status \"#{status}\" for a cross-listing" unless /\A(active|deleted)\z/i.match?(status)
        return if @batch.skip_deletes? && status =~ /deleted/i

        section = @root_account.course_sections.find_by(sis_source_id: section_id)
        raise ImportError, "A cross-listing referenced a non-existent section #{section_id}" unless section

        unless @course && @course.sis_source_id == xlist_course_id
          @course = @root_account.all_courses.find_by(sis_source_id: xlist_course_id)
          if !@course && status =~ /\Aactive\z/i
            # no course with this crosslist id found, make a new course,
            # using the section's current course as a template
            @course = Course.new
            @course.root_account = @root_account
            @course.account_id = section.course.account_id
            @course.name = section.course.name
            @course.course_code = section.course.course_code
            @course.enrollment_term_id = section.course.enrollment_term_id
            @course.start_at = section.course.start_at
            @course.conclude_at = section.course.conclude_at
            @course.restrict_enrollments_to_course_dates = section.course.restrict_enrollments_to_course_dates
            @course.sis_source_id = xlist_course_id
            @course.sis_batch_id = @batch.id
            @course.workflow_state = "claimed"

View on GitHub (pinned to 1c9f0bb801)