RocketChat/Rocket.Chat · error · Meteor.Error
403
403
Error message
User validation failed
What it means
A 403 'User validation failed' raised in onCreateUserAsync when !validateEmailDomain(user) after the onCreateUser callback. In current code validateEmailDomain throws the more specific error-invalid-domain itself, so this branch acts as a legacy safety net for the same condition: the user's email domain is not accepted by Accounts_AllowedDomainsList, or a customized/older validateEmailDomain returned false instead of throwing.
Source
Thrown at apps/meteor/server/lib/auth/startup.js:284
to: destinations,
from: settings.get('From_Email'),
subject: Accounts.emailTemplates.userToActivate.subject(),
html: Accounts.emailTemplates.userToActivate.html({
...options,
name: options.name || options.profile?.name,
email: options.email || user.emails[0].address,
}),
};
await Mailer.send(email);
}
if (!options.skipOnCreateUserCallback) {
await callbacks.run('onCreateUser', options, user);
}
if (!options.skipEmailValidation && !validateEmailDomain(user)) {
throw new Meteor.Error(403, 'User validation failed');
}
return removeEmpty(user);
};
Accounts.onCreateUser(function (...args) {
// Depends on meteor support for Async
return onCreateUserAsync.call(this, ...args);
});
const { insertUserDoc } = Accounts;
Accounts.insertUserDoc = async function (options, user) {
const globalRoles = new Set();
if (Match.test(options.globalRoles, [String]) && options.globalRoles.length > 0) {
options.globalRoles.map((role) => globalRoles.add(role));
}View on GitHub (pinned to b2c16d5842)
Solutions
- Fix Accounts_AllowedDomainsList exactly as for error-invalid-domain (add the exact domain or clear the setting)
- Pass skipEmailValidation: true in options for trusted programmatic creation flows
- If you maintain a fork, make your validateEmailDomain either return true or throw error-invalid-domain instead of returning false
Defensive patterns
Strategy: try-catch
Validate before calling
const isEmailAllowed = (email: string, whitelistSetting: string | undefined): boolean => {
const list = String(whitelistSetting ?? '').split(',').map((d) => d.trim()).filter(Boolean);
return list.length === 0 || list.some((d) => email.toLowerCase().endsWith('@' + d.toLowerCase()));
};
// pre-check before any programmatic user creation when the whitelist is set Try / catch
try {
await Accounts.createUserAsync(options);
} catch (e) {
if (e instanceof Meteor.Error && e.error === 403 && /User validation failed/.test(String(e.reason ?? e.message))) {
// registration policy (domain whitelist) rejected the user: fix Accounts_AllowedDomainsList, do not retry
}
throw e;
} Prevention
- Validate the email domain before every programmatic create (same check as error-invalid-domain)
- If you customize validateEmailDomain, throw error-invalid-domain rather than returning false
- Cover this path in tests for your auth customizations
When it happens
Trigger: Any user-creation path with options.skipEmailValidation falsy where validateEmailDomain returns false or undefined instead of true — typically forked/monkey-patched validation code or older Rocket.Chat versions where the domain check returned false rather than throwing.
Common situations: Same misconfiguration as error-invalid-domain (whitelist too strict, subdomain not listed, programmatic imports); servers running customized auth startup code where validateEmailDomain behavior was changed.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/9d41c907b024ce40.
Report an issue: GitHub.