RocketChat/Rocket.Chat · error · Meteor.Error

String(e)

Error message

String(e)

What it means

Last-resort rethrow in registerUser's catch: Accounts.createUserAsync rejected with a value that is neither a Meteor.Error nor an Error (e.g. a raw string, object, or number from a badly-written async hook). The value is stringified into the error text, so the client receives a Meteor.Error whose 'error' field is that string and there is no meaningful code. Encountering this almost always means a custom hook is rejecting with a non-Error value.

Source

Thrown at apps/meteor/server/meteor-methods/users/registerUser.ts:119

		email: trim(formData.email.toLowerCase()),
		password: formData.pass,
		...(formData.name?.trim() && { name: formData.name?.trim() }),
		reason: formData.reason,
	};

	let userId;
	try {
		userId = await Accounts.createUserAsync(userData);
	} catch (e) {
		if (e instanceof Meteor.Error) {
			throw e;
		}

		if (e instanceof Error) {
			throw new Meteor.Error(e.message);
		}

		throw new Meteor.Error(String(e));
	}

	const reason = trim(formData.reason);
	if (manuallyApproveNewUsers && reason) {
		await Users.setReason(userId, reason);
	}

	try {
		Accounts.sendVerificationEmail(userId, userData.email);
	} catch (error) {
		// throw new Meteor.Error 'error-email-send-failed', 'Error trying to send email: ' + error.message, { method: 'registerUser', message: error.message }
	}

	return userId;
};

Meteor.methods<ServerMethods>({
	async registerUser(formData) {

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Find the rejection source: log the stringified value server-side; it usually names the plugin that threw it.
  2. Fix the offending hook to reject with `new Meteor.Error(code, reason)` (or at least a real Error).
  3. Update or remove legacy accounts-related packages that still use callback-style or throw non-Error values.

Example fix

// before: hook rejects with a string -> Meteor.Error(String(e))
async function quotaHook() { throw 'user limit reached'; }

// after: reject with a coded Meteor.Error
async function quotaHook() { throw new Meteor.Error('error-user-limit-reached', 'License user limit reached'); }
Defensive patterns

Strategy: try-catch

Type guard

const isMeteorError = (e: unknown): e is Meteor.Error =>
  typeof e === 'object' && e !== null && 'error' in e && 'reason' in e;

Try / catch

try {
  await Meteor.callAsync('registerUser', formData);
} catch (e) {
  // Stringified non-Error rejections land in (e as Meteor.Error).error with no 'error-*' code
  const raw = (e as Meteor.Error).error;
  if (typeof raw === 'string' && !raw.startsWith('error-')) {
    logRemote('registerUser: plugin rejected with non-Error value', { raw });
    showError('Registration blocked: ' + raw);
  }
}

Prevention

When it happens

Trigger: A package or app hook does `throw 'user limit reached'` or rejects a promise with a plain object; a corrupted driver callback invoking the callback with an object instead of an Error; outdated community packages incompatible with createUserAsync's promise API.

Common situations: Legacy plugins written for callback-style accounts APIs after migration to async; third-party license/quota plugins throwing strings.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/e1f7e4c4b3d09e88. Report an issue: GitHub.