instructure/canvas-lms · error · ImportError
A user did not pass validation
Error message
A user did not pass validation (user: #{user_row.user_id}, error: #{pseudo.errors.full_messages.join(", ")}) What it means
SisImports::ImportError raised in SIS::UserImporter#process_batch when the Pseudonym fails validation on the final save path (unlike the earlier per-attribute error, this reports ALL errors via pseudo.errors.full_messages joined with ', '). It is wrapped with the sis user_id for identification.
Solutions
- Parse the comma-separated error list in the message to see every failing attribute
- Correct the login_id / authentication provider fields in users.csv for the reported user
- Resolve uniqueness conflicts against existing pseudonyms in the root account
- Review account login/password policy settings if previously-valid imports now fail
Example fix
// before (users.csv) user_id,login_id 1001, // after user_id,login_id 1001,jdoe@school.edu
Defensive patterns
Strategy: try-catch
Validate before calling
raise 'blank login_id' if row[:login_id].to_s.strip.empty? raise 'unknown auth provider' if row[:authentication_provider_id] && !account.authentication_providers.exists?(row[:authentication_provider_id])
Try / catch
begin
importer.add_user(row)
rescue SisImports::ImportError => e
raise unless e.message.start_with?('A user did not pass validation')
errors = e.message[/error: (.+)\)$/, 1].to_s.split(', ')
logger.warn("Pseudonym invalid for #{row[:user_id]}: #{errors.inspect}")
end Prevention
- Pre-check login_id presence and format against account policies
- Verify authentication_provider_id references an existing provider
- Run imports against a staging account to surface pseudonym validation changes early
When it happens
Trigger: During process_batch (called from add_user), when the pseudonym save for the row is invalid — e.g. login missing/blank after transformations, uniqueness failure, or invalid authentication provider settings — the else branch assembles 'A user did not pass validation (user: X, error: ...)' and raises ImportError.
Common situations: Rows where login_id becomes blank after trimming/casing rules; account-level policies (password complexity, login format) rejecting generated logins; uniqueness collisions not caught by the earlier single-error path; integration scripts writing inconsistent authentication_provider_id values.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- # (user # , login # )
- Not Found
- # (user # , login # )
- An email did not pass validation
- An institutional tag category did not pass validation
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/82e3c35d25f1d10c.
Report an issue: GitHub.
Appendix: source
Thrown at lib/sis/user_importer.rb:477
elsif user_row.email.present? && EmailAddressValidator.valid?(user_row.email) == false
message = "The email address associated with user '#{user_row.user_id}' is invalid (email: '#{user_row.email}')"
@messages << SisBatch.build_error(user_row.csv, message, sis_batch: @batch, row: user_row.lineno, row_info: user_row.row)
next
end
if pseudo.changed? || (Pseudonym.sis_stickiness_options[:clear_sis_stickiness] && pseudo["stuck_sis_fields"].present?)
pseudo.sis_batch_id = user_row.sis_batch_id if user_row.sis_batch_id
pseudo.sis_batch_id = @batch.id if @batch
if pseudo.valid?
pseudo_changed = pseudo.changed?
if pseudo.save_without_broadcasting
record_sync_for(user) if pseudo_changed
end
else
msg = "A user did not pass validation "
msg += "(" + "user: #{user_row.user_id}, error: "
msg += pseudo.errors.full_messages.join(", ") + ")"
raise ImportError, msg
end
elsif @batch && pseudo.sis_batch_id != @batch.id
@pseudos_to_set_sis_batch_ids << pseudo.id
end
maybe_write_roll_back_data
if is_new_user_with_password_notification
cc.workflow_state = "unconfirmed"
should_sync_user = pseudo.changed? || cc.changed?
if pseudo.save_without_broadcasting
record_sync_for(user) if should_sync_user
if cc.save_without_broadcasting
pseudo.send_registration_notification!
end
end
end
@success_count += 1
endView on GitHub (pinned to 1c9f0bb801)