instructure/canvas-lms · error · ImportError
A new_id, '# ', referenced an existing # and the # with #…
Error message
A new_id, '#{data_change.new_id}', referenced an existing #{type} and the #{type} with #{column} '#{data_change.old_id}' was not updated What it means
check_for_conflicting_ids raises this when a non-blank new_id already matches an existing record of the given type under the scope's SIS column (sis_source_id or sis_user_id for users). Re-keying an old record onto an identifier that is already taken would create a collision, so the update is refused and the old record left untouched.
Solutions
- Free the target identifier first (change the other record to a temporary id), then re-run this change.
- Verify the new_id is not already in use: root_account.all_courses.where(sis_source_id: new_id).exists? etc.
- Remove duplicate/replayed rows from the import batch.
Example fix
// before: new_id already taken user,U1,U-TAKEN // after: two-step swap via temp id user,U1,TEMP-U user,U-TAKEN,U1 user,TEMP-U,U-TAKEN
Defensive patterns
Strategy: validation
Validate before calling
scope = case dc.type
when 'user' then root_account.pseudonyms
when 'course' then root_account.all_courses
# ... etc
end
column = dc.type == 'user' ? :sis_user_id : :sis_source_id
if dc.new_id.present? && scope.where(column => dc.new_id).exists?
raise ArgumentError, "new_id already taken: #{dc.new_id}"
end
process_change_sis_id(dc) Try / catch
begin
process_change_sis_id(dc)
rescue SIS::ImportError => e
if e.message.include?('referenced an existing')
logger.warn("collision on new_id #{dc.new_id}; row skipped")
end
end Prevention
- Check uniqueness of new_id in the target scope before generating the row.
- For id swaps, stage through a temporary unique id.
- Deduplicate batch files to avoid replaying already-applied changes.
When it happens
Trigger: change_sis_id row where scope.where(column => new_id).exists? is true — e.g. swapping two records' SIS ids without first freeing one, or retrying a change that already succeeded.
Common situations: Renaming SIS ids in circles (A→B while B still holds B), duplicate rows in the same CSV, idempotent re-runs of an import that already applied the rename.
Related errors
- A new_integration_id, '#
- A student referenced a non-existent user #
- An old_id, '# ', referenced a different # than the…
- An old_id, '# ', referenced a non-existent # and was not…
- An old_integration_id, '#
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/4ec3879f668865c4.
Report an issue: GitHub.
Appendix: source
Thrown at lib/sis/change_sis_id_importer.rb:128
if data_change.new_id.present?
updates[column] = data_change.new_id
end
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?View on GitHub (pinned to 1c9f0bb801)