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

  1. Fix Accounts_AllowedDomainsList exactly as for error-invalid-domain (add the exact domain or clear the setting)
  2. Pass skipEmailValidation: true in options for trusted programmatic creation flows
  3. 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

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.