{"record":{"id":"79f707bd9e09c84a","repo":"instructure/canvas-lms","slug":"an-email-did-not-pass-validation-user-row-email-error-cc","errorCode":null,"errorMessage":"An email did not pass validation (#{user_row.email}, error: #{cc.errors.full_messages.join(\", \")})","messagePattern":"An email did not pass validation \\(#(.+?), error: #(.+?)\\)","errorType":"validation","errorClass":"ImportError","httpStatus":null,"severity":"error","filePath":"lib/sis/user_importer.rb","lineNumber":431,"sourceCode":"              sis_cc = nil\n            end\n            cc = sis_cc || other_cc || user.communication_channels.new\n            cc.user_id = user.id\n            cc.pseudonym_id = pseudo.id\n            cc.path = user_row.email\n            cc.bounce_count = 0 if cc.path_changed?\n            cc.workflow_state = (status == \"deleted\") ? \"retired\" : \"active\"\n            newly_active = cc.path_changed? || (cc.active? && cc.workflow_state_changed?)\n            if cc.changed?\n              if cc.valid? && cc.save_without_broadcasting\n                record_sync_for(user)\n                cc_data = SisBatchRollBackData.build_data(sis_batch: @batch, context: cc)\n                @roll_back_data << cc_data if cc_data\n              else\n                msg = \"An email did not pass validation \"\n                msg += \"(\" + \"#{user_row.email}, error: \"\n                msg += cc.errors.full_messages.join(\", \") + \")\"\n                raise ImportError, msg\n              end\n              user.touch unless user_touched\n              user.clear_email_cache!\n            end\n            pseudo.sis_communication_channel_id = pseudo.communication_channel_id = cc.id\n\n            if newly_active && @root_account.feature_enabled?(:self_service_user_merge)\n              user_ids = ccs.map(&:user_id)\n              pseudo_scope = Pseudonym.active.where(user_id: user_ids).group(:user_id)\n              active_pseudo_counts = pseudo_scope.count\n              sis_pseudo_counts = pseudo_scope.where(\"account_id = ? AND sis_user_id IS NOT NULL\", @root_account).count\n\n              other_ccs = ccs.reject do |other|\n                cc_user_id = other.user_id\n                same_user = cc_user_id == user.id\n                no_active_pseudos = active_pseudo_counts.fetch(cc_user_id, 0) == 0\n                active_sis_pseudos = sis_pseudo_counts.fetch(cc_user_id, 0) != 0\n","sourceCodeStart":413,"sourceCodeEnd":449,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/lib/sis/user_importer.rb#L413-L449","documentation":"SisImports::ImportError raised in SIS::UserImporter#process_batch when the CommunicationChannel created for the user's email address fails validation (cc.save! path fails / cc invalid). All of cc.errors.full_messages are joined into the message together with the offending email value.","triggerScenarios":"During process_batch (called from add_user), the row supplies an email column and the importer builds a CommunicationChannel; when the channel is invalid (e.g. malformed or blank email address per EmailValidator / format rules), the else branch raises ImportError with the email and the full error list.","commonSituations":"Emails with stray spaces, commas, or missing TLDs in users.csv; empty email strings for users where email is required; case or plus-addressing variants tripping stricter validators; data migrated from another SIS with loosely validated addresses.","solutions":["Fix the email value reported in the error message so it is a syntactically valid address","Trim whitespace and remove illegal characters (spaces, multiple @, unescaped commas) from the email column","If email is optional for this user, remove the value rather than sending an invalid one (subject to account policy)","Add CSV-level email format pre-validation before upload"],"exampleFix":"// before (users.csv)\nuser_id,email\n1001,jane doe@school edu\n\n// after\nuser_id,email\n1001,jane.doe@school.edu","handlingStrategy":"validation","validationCode":"EMAIL_RE = URI::MailTo::EMAIL_REGEXP\nrows.each do |r|\n  next if r[:email].blank?\n  raise \"invalid email #{r[:email]}\" unless EMAIL_RE.match?(r[:email].strip)\nend","typeGuard":"def valid_email?(v) = v.is_a?(String) && URI::MailTo::EMAIL_REGEXP.match?(v.strip)","tryCatchPattern":"begin\n  importer.add_user(row)\nrescue SisImports::ImportError => e\n  raise unless e.message.start_with?('An email did not pass validation')\n  logger.warn(\"Skipping row with bad email: #{e.message}\")\nend","preventionTips":["Validate the email column with a regex before uploading the SIS batch","Trim whitespace and reject values containing spaces or commas","Keep emails optional-empty rather than invalid-filled when unknown"],"tags":["sis-import","email-validation","communication-channel","ruby"],"backgroundTag":"invalid-argument-format","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"}