RocketChat/Rocket.Chat · error · Error
error-invalid-user
error-invalid-user
Error message
error-invalid-user
What it means
executeUnbanUserFromRoom requires the target user to have a username because the unban flow writes a 'user-unbanned' system message attributed to that username. A user object without username (undefined) triggers Error 'error-invalid-user'.
Source
Thrown at apps/meteor/server/lib/rooms/executeUnbanUserFromRoom.ts:15
import { Message } from '@rocket.chat/core-services';
import { isBannedSubscription, isInviteSubscription, type IUser } from '@rocket.chat/core-typings';
import { Rooms, Subscriptions, Users } from '@rocket.chat/models';
import { afterUnbanFromRoomCallback } from '../callbacks/afterUnbanFromRoomCallback';
import { notifyOnRoomChangedById, notifyOnSubscriptionChanged } from '../notifyListener';
export const executeUnbanUserFromRoom = async function (rid: string, user: IUser, byUser: IUser): Promise<void> {
const room = await Rooms.findOneById(rid);
if (!room) {
throw new Error('error-invalid-room');
}
if (!user.username) {
throw new Error('error-invalid-user');
}
const subscription = await Subscriptions.findOneByRoomIdAndUserId(rid, user._id);
if (!subscription) {
throw new Error('error-invalid-subscription');
}
// if the subscription is an invite it means we were unbanned and then invited again, then
// the invite was accepted and we receive a leave event (meaning the user was unbanned), so
// at this point we just need send the message to say the user was unbanned.
if (isInviteSubscription(subscription)) {
await Message.saveSystemMessage('user-unbanned', rid, user.username, user, {
u: { _id: byUser._id, username: byUser.username },
});
return;
}
View on GitHub (pinned to b2c16d5842)
Solutions
- Fetch the full user document (no projection) before the unban call
- Ensure the target user has a username (finish custom-auth provisioning) before moderation actions
- Type calling-code parameters as Pick<IUser, '_id' | 'username'>
Example fix
// before
await executeUnbanUserFromRoom(rid, user, byUser);
// after
if (!user.username) {
throw new Meteor.Error('error-invalid-user', 'Target user has no username');
}
await executeUnbanUserFromRoom(rid, user, byUser); Defensive patterns
Strategy: type-guard
Validate before calling
if (!user.username) {
throw new Meteor.Error('error-invalid-user', 'Target user has no username');
}
await executeUnbanUserFromRoom(rid, user, byUser); Type guard
const hasUsername = (user: Pick<IUser, '_id'> | IUser): user is IUser & { username: string } =>
Boolean(user.username); Try / catch
try {
await executeUnbanUserFromRoom(rid, user, byUser);
} catch (err) {
if (err instanceof Error && err.message === 'error-invalid-user') {
// re-fetch the full user document and retry once
}
throw err;
} Prevention
- Fetch full user documents for moderation flows
- Guard username before any moderation method call
- Add a DB invariant/test asserting users always have usernames post-provisioning
When it happens
Trigger: Passing a user document fetched with a projection that omits username, or a user record that genuinely has no username (custom auth not yet finalized), into the unban flow.
Common situations: Federation/app code passing partial user objects; user records mid-provisioning; queries using projections like { _id: 1 } reused across features.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/b344201cb38b1338.
Report an issue: GitHub.