RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-user

error-invalid-user

Error message

Invalid user

What it means

After the room and permission gates, removeRoomModerator loads the target via Users.findOneById(userId) and requires a non-empty username. Throws error-invalid-user when the target user document does not exist or has no username (deleted user or incomplete record). The second argument is the user's _id, not their username.

Source

Thrown at apps/meteor/server/meteor-methods/rooms/removeRoomModerator.ts:43

	check(userId, String);

	const room = await Rooms.findOneById(rid, { projection: { t: 1, federated: 1, federation: 1 } });
	if (!room) {
		throw new Meteor.Error('error-invalid-room', 'Invalid room', {
			method: 'removeRoomModerator',
		});
	}

	if (!(await hasPermissionAsync(fromUserId, 'set-moderator', rid)) && !isRoomFederated(room)) {
		throw new Meteor.Error('error-not-allowed', 'Not allowed', {
			method: 'removeRoomModerator',
		});
	}

	const user = await Users.findOneById(userId);

	if (!user?.username) {
		throw new Meteor.Error('error-invalid-user', 'Invalid user', {
			method: 'removeRoomModerator',
		});
	}

	const subscription = await Subscriptions.findOneByRoomIdAndUserId(rid, user._id);

	if (!subscription) {
		throw new Meteor.Error('error-invalid-room', 'Invalid room', {
			method: 'removeRoomModerator',
		});
	}

	if (subscription.roles && (!Array.isArray(subscription.roles) || !subscription.roles.includes('moderator'))) {
		throw new Meteor.Error('error-user-not-moderator', 'User is not a moderator', {
			method: 'removeRoomModerator',
		});
	}

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Resolve the target's _id first (Users.findOneByUsername on the client, or GET /api/v1/users.info) and pass that
  2. Confirm the target still exists and has a username before showing role actions
  3. Remove stale users from pickers when the users.info lookup fails

Example fix

// before
await Meteor.callAsync('removeRoomModerator', rid, targetUsername);

// after
const user = Users.findOne({ username: targetUsername });
if (!user?._id) throw new Error('target missing');
await Meteor.callAsync('removeRoomModerator', rid, user._id);
Defensive patterns

Strategy: validation

Validate before calling

const target = Users.findOne({ _id: userId });
if (!target?.username) {
  // target missing or incomplete; do not call
}

Type guard

const isRemovableUser = (u: { _id?: string; username?: string } | null | undefined): u is { _id: string; username: string } =>
  Boolean(u?._id && u?.username);

Try / catch

try {
  await Meteor.callAsync('removeRoomModerator', rid, userId);
} catch (err) {
  if (err instanceof Meteor.Error && err.error === 'error-invalid-user') {
    // target user not found: drop them from pickers, refresh members
  }
}

Prevention

When it happens

Trigger: Passing the target's username instead of _id; the target user was deleted before the call landed; a user record created without a username (registration never finished).

Common situations: Custom member-management UI storing usernames where IDs are expected; target deleted by an admin while the role dialog was open; imported users missing usernames.

Understand the failure class

Background: "User not found", "Invalid user", and "does not exist": what missing-user lookup errors mean across Rocket.Chat, LiteLLM, Phabricator, rustfs, and pnpm — this error's family across 10 libraries.

Related errors


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