RocketChat/Rocket.Chat · error · Meteor.Error
(error as any)?.message
Error message
(error as any)?.message
What it means
addUserToRoom re-wraps any error thrown by the beforeAddUserToRoom callback chain (the patch function plus callbacks) into a new Meteor.Error whose code is the original error's message. The resulting error code is therefore whatever a callback threw (e.g. 'error-invalid-user', 'unauthorized', or a custom string), and the original error's structure and details are lost.
Source
Thrown at apps/meteor/server/lib/rooms/addUserToRoom.ts:64
if (!userToBeAdded) {
throw new Meteor.Error('user-not-found');
}
if (
!(await roomDirectives.allowMemberAction(room, RoomMemberActions.JOIN, userToBeAdded._id)) &&
!(await roomDirectives.allowMemberAction(room, RoomMemberActions.INVITE, userToBeAdded._id))
) {
return;
}
try {
const inviterUser = inviter && ((await Users.findOneById(inviter._id)) || undefined);
// Not "duplicated": we're moving away from callbacks so this is a patch function. We should migrate the next one to be a patch or use this same patch, instead of calling both
await beforeAddUserToRoomPatch([userToBeAdded.username!], room, inviterUser);
await beforeAddUserToRoom.run({ user: userToBeAdded, inviter: inviterUser }, room);
} catch (error) {
throw new Meteor.Error((error as any)?.message);
}
// TODO: are we calling this twice?
await callbacks.run('beforeAddedToRoom', { user: userToBeAdded, inviter });
try {
await Apps.self?.triggerEvent(AppEvents.IPreRoomUserJoined, room, userToBeAdded, inviter);
} catch (error: any) {
if (error.name === AppsEngineException.name) {
throw new Meteor.Error('error-app-prevented', error.message);
}
throw error;
}
// for federation rooms we stop here since everything else will be handled by the federation invite flow
if (isRoomNativeFederated(room)) {View on GitHub (pinned to b2c16d5842)
Solutions
- Search for code registering on 'beforeAddUserToRoom' (callbacks.add / patch functions) to find which one throws.
- Fix the underlying denial that the callback reports.
- When catching, inspect err.error - it carries the callback's original message; consider an upstream fix to preserve the error object.
Example fix
// before: callback throws plain errors that become opaque codes
throw new Error('user is banned');
// after: throw a Meteor.Error so the outer wrap keeps a stable code
throw new Meteor.Error('error-user-banned', 'User is banned'); Defensive patterns
Strategy: try-catch
Try / catch
try {
await addUserToRoom(rid, user, inviter);
} catch (error: any) {
// error.error here is the *message* thrown by a beforeAddUserToRoom callback.
// Log error.error and error.reason, then inspect registered 'beforeAddUserToRoom'
// callbacks/patches to find which one vetoed the add.
} Prevention
- In your own 'beforeAddUserToRoom' callbacks, throw Meteor.Errors with stable codes - they become the outer error code here.
- Audit registered callbacks on 'beforeAddUserToRoom' when odd error codes appear.
- Do not rely on structured details from the wrapped error - this wrap drops them.
When it happens
Trigger: Any 'beforeAddUserToRoom' callback (core patches blocking banned/deactivated users, apps, custom server code) throws during addUserToRoom; its message surfaces as the outer Meteor error code.
Common situations: Developers puzzled by unexpected error codes when adding users; custom callbacks denying membership; debugging which callback vetoed the add.
Related errors
- Only channels, private groups and direct messages can be cre
- Invalid user
- Message converter not found
- Room converter not found
- Room id not found
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/127133de796286cc.
Report an issue: GitHub.