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

  1. Search for code registering on 'beforeAddUserToRoom' (callbacks.add / patch functions) to find which one throws.
  2. Fix the underlying denial that the callback reports.
  3. 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

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


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