{"record":{"id":"eb09c0817eedfdff","repo":"n8n-io/n8n","slug":"cannot-save-user-this-email-provided-email-i","errorCode":null,"errorMessage":"Cannot save user <${this.email}>: Provided email is invalid","messagePattern":"Cannot save user <(.+?)>: Provided email is invalid","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/db/src/entities/user.ts","lineNumber":91,"sourceCode":"\t@OneToMany('SharedCredentials', 'user')\n\tsharedCredentials: SharedCredentials[];\n\n\t@OneToMany('ProjectRelation', 'user')\n\tprojectRelations: ProjectRelation[];\n\n\t@Column({ type: Boolean, default: false })\n\tdisabled: boolean;\n\n\t@BeforeInsert()\n\t@BeforeUpdate()\n\tpreUpsertHook(): void {\n\t\tthis.email = this.email?.toLowerCase() ?? null;\n\n\t\t// Validate email if present (including empty strings)\n\t\tif (this.email !== null && this.email !== undefined) {\n\t\t\tconst result = isValidEmail(this.email);\n\t\t\tif (!result) {\n\t\t\t\tthrow new Error(`Cannot save user <${this.email}>: Provided email is invalid`);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Column({ type: Boolean, default: false })\n\tmfaEnabled: boolean;\n\n\t@Column({ type: String, nullable: true })\n\tmfaSecret?: string | null;\n\n\t@Column({ type: 'simple-array', default: '' })\n\tmfaRecoveryCodes: string[];\n\n\t@Column({ type: 'date', nullable: true })\n\tlastActiveAt?: Date | null;\n\n\t/**\n\t * Whether the user is pending setup completion.","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/db/src/entities/user.ts#L73-L109","documentation":"Thrown from User entity's @BeforeInsert/@BeforeUpdate hook (preUpsertHook) when the email field, after lowercasing, fails isValidEmail(). Fires on any persistence path (TypeORM insert/update) where email is set — including empty strings, since the hook explicitly validates non-null/undefined values. Bare `new Error(...)`, not an n8n error class.","triggerScenarios":"Creating or updating a User with a malformed email (missing @, invalid TLD, control chars), an empty string '', or a value that fails the project's isValidEmail regex/validation. Triggered via repositories, services, or any direct TypeORM save on the User entity.","commonSituations":"SSO/import scripts feeding dirty data; a signup form bypassing client-side validation; an LDAP/SAML sync mapping an empty/missing mail attribute to email; migrations seeding users with placeholder addresses like 'user@'; tests using 'invalid' as an email.","solutions":["Run isValidEmail() (from @n8n/db or n8n-workflow email utils) on the value BEFORE attempting to save the user.","Normalize upstream: if email comes from LDAP/SAML/SCIM, reject or default the record when mail is absent/invalid.","Treat empty email explicitly — decide between null (allowed) and an actual address; never pass '' for a required email.","For bulk imports, validate the entire batch first and report offending rows rather than failing row-by-row."],"exampleFix":"// before\nawait userRepo.save({ email: input.email });\n\n// after\nif (input.email !== null && input.email !== undefined && !isValidEmail(input.email)) {\n  throw new UserError(`Refusing to save user: email '${input.email}' is invalid`);\n}\nawait userRepo.save({ email: input.email?.toLowerCase() ?? null });","handlingStrategy":"validation","validationCode":"import { isValidEmail } from '@n8n/db'; // or wherever exposed\nfunction assertEmail(email: string | null | undefined) {\n  if (email !== null && email !== undefined && !isValidEmail(email)) {\n    throw new Error(`Invalid email: '${email}'`);\n  }\n}","typeGuard":"function isValidUserEmail(email: unknown): email is string {\n  return typeof email === 'string' && isValidEmail(email);\n}","tryCatchPattern":"try {\n  await userRepo.save(user);\n} catch (err) {\n  if (err instanceof Error && /Cannot save user .* Provided email is invalid/.test(err.message)) {\n    // reject the input, surface a field-level error to the user\n  } else throw err;\n}","preventionTips":["Validate email at every ingress (API, SSO sync, import) before reaching the entity layer.","Never pass '' for an email; use null when absent.","For SSO/LDAP imports, default or skip records with missing mail attributes."],"tags":["validation","user","email","entity","typeorm"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}