instructure/canvas-lms · error · ImportError

Improper status "# " for a cross-listing

Error message

Improper status "#{status}" for a cross-listing

What it means

The SIS cross-listing importer validates that each row's status is exactly 'active' or 'deleted' (case-insensitive) and raises ImportError for anything else. The status drives whether the section is cross-listed into the target course or uncross-listed, so unknown values are rejected before any data is touched.

Solutions

  1. Use only 'active' or 'deleted' as status values in the crosslist SIS file
  2. Strip whitespace and normalize casing of the status column before import
  3. Map third-party status vocabulary to active/deleted before calling add_crosslist
  4. Add a pre-import CSV lint that rejects rows with statuses outside the allowed set

Example fix

# before
xlist.add_crosslist(course_id, section_id, row['status']) # "remove"
# after
status = row['status'].to_s.strip.downcase
status = 'deleted' if status == 'remove'
xlist.add_crosslist(course_id, section_id, status)
Defensive patterns

Strategy: validation

Validate before calling

unless status.to_s.match?(/\A(active|deleted)\z/i)
  raise ArgumentError, "status must be active or deleted, got #{status.inspect}"
end

Try / catch

begin
  xlist.add_crosslist(xlist_course_id, section_id, status)
rescue SIS::ImportError => e
  Rails.logger.warn("Bad crosslist status: #{e.message}")
end

Prevention

When it happens

Trigger: Calling add_crosslist(xlist_course_id, section_id, status) where status is not /\A(active|deleted)\z/i — e.g. 'delete', 'Active ', 'enabled', or a nil status from a blank CSV cell. Note the second guard at line 121 also catches statuses that pass initial regex checks only if the regex changed; normally this fires for unexpected case values hitting the case statement.

Common situations: Hand-edited SIS CSVs using synonyms like 'remove' or 'deactivate'; integrations sending boolean-ish values ('true'/'false'); locale-transformed exports; trailing whitespace or BOM characters contaminating the status column.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at lib/sis/xlist_importer.rb:55

    end

    class Work
      attr_accessor :success_count, :course_ids_to_update_associations

      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

View on GitHub (pinned to 1c9f0bb801)