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

  1. Parse the comma-separated error list in the message to see every failing attribute
  2. Correct the login_id / authentication provider fields in users.csv for the reported user
  3. Resolve uniqueness conflicts against existing pseudonyms in the root account
  4. 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

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


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
        end

View on GitHub (pinned to 1c9f0bb801)