instructure/canvas-lms · error · ImportError

A new_integration_id, '#

Error message

A new_integration_id, '#{data_change.new_integration_id}', referenced an existing #{type} and the #{type} with integration_id '#{data_change.old_integration_id}' was not updated

What it means

check_for_conflicting_ids raises this when a non-blank new_integration_id already exists on another record of the type being changed (scope.where(integration_id: new_integration_id).exists?). Integration ids must remain unique within the account, so assigning an already-used one is rejected and no update occurs.

Solutions

  1. Clear the integration_id on the conflicting record first (new_integration_id '<delete>' on that record), then assign it.
  2. Query scope.where(integration_id: new_integration_id).exists? before submitting to detect the holder.
  3. Deduplicate rows in the importing system to avoid replays.

Example fix

// before
course,C1,C2,,INT-DUP
// after: free INT-DUP first
course,C-SRC,,,<delete>
course,C1,C2,,INT-DUP
Defensive patterns

Strategy: validation

Validate before calling

if dc.new_integration_id.present? &&
   scope_for(dc.type).where(integration_id: dc.new_integration_id).exists?
  raise ArgumentError, 'new_integration_id already in use'
end
process_change_sis_id(dc)

Try / catch

begin
  process_change_sis_id(dc)
rescue SIS::ImportError => e
  logger.warn("integration_id collision: #{e.message}")
end

Prevention

When it happens

Trigger: change_sis_id row where new_integration_id matches an existing row's integration_id — e.g. moving an integration_id from record X to record Y while X still holds it, or replaying a previously applied change.

Common situations: Two SIS systems writing the same integration_id, idempotent re-imports, or re-pointing an integration id during a migration without clearing it from the source record first.

Related errors


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

Appendix: source

Thrown at lib/sis/change_sis_id_importer.rb:131

        if data_change.new_integration_id.present?
          updates["integration_id"] = data_change.new_integration_id
          if data_change.new_integration_id == "<delete>"
            updates["integration_id"] = nil
          end
        end
        updates
      end

      def check_for_conflicting_ids(column, details, type, data_change)
        if type == "group_category" && (data_change.old_integration_id || data_change.new_integration_id)
          raise ImportError, "Group categories should not have integration IDs."
        end

        check_new = details[:scope].where(column => data_change.new_id).exists? if data_change.new_id.present?
        raise ImportError, "A new_id, '#{data_change.new_id}', referenced an existing #{type} and the #{type} with #{column} '#{data_change.old_id}' was not updated" if check_new

        check_int = details[:scope].where(integration_id: data_change.new_integration_id).exists? if data_change.new_integration_id.present?
        raise ImportError, "A new_integration_id, '#{data_change.new_integration_id}', referenced an existing #{type} and the #{type} with integration_id '#{data_change.old_integration_id}' was not updated" if check_int
      end

      def find_item_to_update(column, details, type, data_change)
        if data_change.old_id.present?
          old_item = details[:scope].find_by(column => data_change.old_id)
        end
        if data_change.old_integration_id.present?
          old_int_item = details[:scope].find_by(integration_id: data_change.old_integration_id)
        end
        if data_change.old_id.present? && data_change.old_integration_id.present?
          raise ImportError, "An old_id, '#{data_change.old_id}', referenced a different #{type} than the old_integration_id, '#{data_change.old_integration_id}'" unless old_item == old_int_item

          return old_item
        end
        if data_change.old_id.present? && data_change.old_integration_id.blank?
          raise ImportError, "An old_id, '#{data_change.old_id}', referenced a non-existent #{type} and was not changed." unless old_item

          return old_item

View on GitHub (pinned to 1c9f0bb801)