instructure/canvas-lms · error · ImportError

No old_id or old_integration_id given for change_sis_id

Error message

No old_id or old_integration_id given for change_sis_id

What it means

process_change_sis_id raises this when both old_id and old_integration_id are blank. The importer must be told which existing record to re-key; with neither identifier it cannot find the row to update. Checked immediately after the type check.

Solutions

  1. Provide old_id (the current sis_source_id/sis_user_id) for the record.
  2. Or provide old_integration_id if the record is identified by its integration_id.
  3. Verify column order in the change_sis_id CSV matches the documented header.

Example fix

// before
user,,new-sis-id
// after
user,old-sis-id,new-sis-id
Defensive patterns

Strategy: validation

Validate before calling

if dc.old_id.blank? && dc.old_integration_id.blank?
  raise ArgumentError, 'old_id or old_integration_id required'
end
process_change_sis_id(dc)

Try / catch

begin
  process_change_sis_id(dc)
rescue SIS::ImportError => e
  errors << { row: dc, error: e.message }
end

Prevention

When it happens

Trigger: A change_sis_id row where the old_id column is empty AND the old_integration_id column is empty (e.g. only new_id supplied).

Common situations: CSV export scripts that only emit the new value, template rows filled in partially, or confusion between old_id/old_integration_id vs new_id/new_integration_id column ordering.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at lib/sis/change_sis_id_importer.rb:69

    end

    class Work
      attr_accessor :success_count,
                    :things_to_update_batch_ids,
                    :users_to_sync

      def initialize(batch, root_account, logger)
        @batch = batch
        @root_account = root_account
        @logger = logger
        @success_count = 0
        @things_to_update_batch_ids = {}
        @users_to_sync = Set.new
      end

      def process_change_sis_id(data_change)
        raise ImportError, "No type given for change_sis_id" if data_change.type.blank?
        raise ImportError, "No old_id or old_integration_id given for change_sis_id" if data_change.old_id.blank? && data_change.old_integration_id.blank?
        raise ImportError, "No new_id or new_integration_id given for change_sis_id" if data_change.new_id.blank? && data_change.new_integration_id.blank?

        type = data_change.type.downcase.strip
        things_to_update_batch_ids[type] ||= Set.new

        types = {
          "user" => { scope: @root_account.pseudonyms, column: :sis_user_id },
          "course" => { scope: @root_account.all_courses },
          "section" => { scope: @root_account.course_sections },
          "term" => { scope: @root_account.enrollment_terms },
          "account" => { scope: @root_account.all_accounts },
          "group" => { scope: @root_account.all_groups },
          "group_category" => { scope: @root_account.all_group_categories },
        }

        details = types[type]
        raise ImportError, "Invalid type '#{type}' for change_sis_id" unless details

View on GitHub (pinned to 1c9f0bb801)