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
- Find the rejection source: log the stringified value server-side; it usually names the plugin that threw it.
- Fix the offending hook to reject with `new Meteor.Error(code, reason)` (or at least a real Error).
- 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
- Enforce that all accounts hooks reject with Error/Meteor.Error instances (lint rule or code review)
- Log the stringified value server-side to identify the offending plugin quickly
- Keep accounts-related community packages updated for async createUser compatibility
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
- e.message
- error-user-registration-disabled
- error-user-registration-secret
- error-registering-provider
- error-importer-not-defined
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/e1f7e4c4b3d09e88.
Report an issue: GitHub.