{"record":{"id":"4379fc0893f65f19","repo":"instructure/canvas-lms","slug":"pseudo-errors-first-full-message-user-user-row-user-id-login","errorCode":null,"errorMessage":"#{pseudo.errors.first.full_message} (user #{user_row.user_id}, login #{user_row.login_id})","messagePattern":"#(.+?) \\(user #(.+?), login #(.+?)\\)","errorType":"validation","errorClass":"ImportError","httpStatus":null,"severity":"error","filePath":"lib/sis/user_importer.rb","lineNumber":359,"sourceCode":"                # need to call save first so we can get the user id\n                record_sync_for(user) if user_saved && user_changed\n                if !user_saved && !user.errors.empty?\n                  message = generate_user_warning(user.errors.first.join(\" \"), user_row.user_id, user_row.login_id)\n                  raise ImportError, message\n                end\n              elsif @batch\n                @users_to_set_sis_batch_ids << user.id\n              end\n              pseudo.user_id = user.id\n              if pseudo.changed?\n                pseudo.sis_batch_id = @batch.id if @batch\n                if pseudo.save_without_broadcasting\n                  record_sync_for(user)\n                  p_data = SisBatchRollBackData.build_data(sis_batch: @batch, context: pseudo)\n                  @roll_back_data << p_data if p_data\n                elsif !pseudo.errors.empty?\n                  message = generate_user_warning(pseudo.errors.first.full_message, user_row.user_id, user_row.login_id)\n                  raise ImportError, message\n                end\n              end\n            end\n          rescue ImportError\n            @messages << SisBatch.build_error(user_row.csv, message, sis_batch: @batch, row: user_row.lineno, row_info: user_row.row)\n            next\n          rescue => e\n            # something broke\n            error = Canvas::Errors.capture_exception(:sis_import, e)\n            er = error[:error_report]\n            message = generate_user_warning(\"Something broke with this user. Contact Support with ErrorReport id: #{er}\", user_row.user_id, user_row.login_id)\n            @messages << SisBatch.build_error(user_row.csv, message, sis_batch: @batch, row: user_row.lineno, backtrace: e.backtrace, row_info: user_row.row)\n            next\n          end\n\n          @users_to_add_account_associations << user.id if should_add_account_associations\n          @users_to_update_account_associations << user.id if should_update_account_associations\n","sourceCodeStart":341,"sourceCodeEnd":377,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/lib/sis/user_importer.rb#L341-L377","documentation":"SisImports::ImportError raised in SIS::UserImporter#process_batch when pseudo.save_without_broadcasting fails validation on the user's Pseudonym (login record). The first error's full_message is wrapped with the sis user_id and login_id via generate_user_warning and raised; the surrounding rescue logs it into the batch messages for that row.","triggerScenarios":"During process_batch (called from add_user), the Pseudonym for the row fails validation on save — most commonly a blank login_id, a login_id already taken by another pseudonym in the account (unique-scope violation), or an invalid sis_user_id. pseudo.errors.first.full_message is then raised as ImportError.","commonSituations":"Duplicate login_id across two CSV rows or colliding with an existing non-SIS user; empty login column for a new user; case/whitespace variants of an existing login; login format rules (e.g. must be email-like) enforced by account settings.","solutions":["Check the embedded error message for which pseudonym attribute failed (typically 'login')","Ensure login_id is present and unique per root account in users.csv; deduplicate rows","Search existing users for the conflicting login and resolve the collision (rename or delete the stale record)","Verify the login matches any account-level login format requirements"],"exampleFix":"// before: two rows share a login\nuser_id,login_id\n1001,jdoe\n1002,jdoe\n\n// after\nuser_id,login_id\n1001,jdoe\n1002,jdoe2","handlingStrategy":"validation","validationCode":"logins = rows.map { |r| r[:login_id].to_s.strip.downcase }\nif logins.uniq.size != logins.size\n  raise 'duplicate login_id values in users.csv'\nend\nraise 'blank login_id' if logins.include?('')","typeGuard":null,"tryCatchPattern":"begin\n  importer.add_user(row)\nrescue SisImports::ImportError => e\n  raise unless e.message.include?('has already been taken')\n  # route duplicate-login rows to a reconciliation step\nend","preventionTips":["Deduplicate login_id values across the CSV and against the account before import","Normalize login casing/whitespace at CSV generation time","Check account login format policies before choosing login schemes"],"tags":["sis-import","activerecord-validation","pseudonym","ruby","duplicate-login"],"backgroundTag":"schema-validation-failed","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}