n8n-io/n8n · error · Error
Cannot save user <${this.email}>: Provided email is invalid
Error message
Cannot save user <${this.email}>: Provided email is invalid What it means
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.
Source
Thrown at packages/@n8n/db/src/entities/user.ts:91
@OneToMany('SharedCredentials', 'user')
sharedCredentials: SharedCredentials[];
@OneToMany('ProjectRelation', 'user')
projectRelations: ProjectRelation[];
@Column({ type: Boolean, default: false })
disabled: boolean;
@BeforeInsert()
@BeforeUpdate()
preUpsertHook(): void {
this.email = this.email?.toLowerCase() ?? null;
// Validate email if present (including empty strings)
if (this.email !== null && this.email !== undefined) {
const result = isValidEmail(this.email);
if (!result) {
throw new Error(`Cannot save user <${this.email}>: Provided email is invalid`);
}
}
}
@Column({ type: Boolean, default: false })
mfaEnabled: boolean;
@Column({ type: String, nullable: true })
mfaSecret?: string | null;
@Column({ type: 'simple-array', default: '' })
mfaRecoveryCodes: string[];
@Column({ type: 'date', nullable: true })
lastActiveAt?: Date | null;
/**
* Whether the user is pending setup completion.View on GitHub (pinned to 5ac6606e81)
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.
Example fix
// before
await userRepo.save({ email: input.email });
// after
if (input.email !== null && input.email !== undefined && !isValidEmail(input.email)) {
throw new UserError(`Refusing to save user: email '${input.email}' is invalid`);
}
await userRepo.save({ email: input.email?.toLowerCase() ?? null }); Defensive patterns
Strategy: validation
Validate before calling
import { isValidEmail } from '@n8n/db'; // or wherever exposed
function assertEmail(email: string | null | undefined) {
if (email !== null && email !== undefined && !isValidEmail(email)) {
throw new Error(`Invalid email: '${email}'`);
}
} Type guard
function isValidUserEmail(email: unknown): email is string {
return typeof email === 'string' && isValidEmail(email);
} Try / catch
try {
await userRepo.save(user);
} catch (err) {
if (err instanceof Error && /Cannot save user .* Provided email is invalid/.test(err.message)) {
// reject the input, surface a field-level error to the user
} else throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Value "${value}" is not a number.
- SelectQueryBuilder.addOrderBy "order" can accept only "ASC"
- SelectQueryBuilder.addOrderBy "nulls" can accept only "NULLS
- Provided "limit" value is not a number. Please provide a num
- Provided "offset" value is not a number. Please provide a nu
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/eb09c0817eedfdff.
Report an issue: GitHub.