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
- Resolve the target's _id first (Users.findOneByUsername on the client, or GET /api/v1/users.info) and pass that
- Confirm the target still exists and has a username before showing role actions
- 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
- Resolve targets by _id from a fresh lookup
- Pass user _id, never username, as the second argument
- Drop users from selection UI when lookup fails
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.