RocketChat/Rocket.Chat · error · Error

User must have a username to be banned from the room

Error message

User must have a username to be banned from the room

What it means

Plain Error thrown by performUserBan when the user record has no username. The ban flow writes system messages and member-list mutations keyed by username, so it refuses to continue for a user without one. Note this path runs for federation-triggered bans and similar external events.

Source

Thrown at apps/meteor/server/lib/rooms/banUserFromRoom.ts:23

import { afterBanFromRoomCallback } from '../callbacks/afterBanFromRoomCallback';
import { notifyOnRoomChangedById, notifyOnSubscriptionChanged } from '../notifyListener';
import { removeUserFromRolesAsync } from '../roles/removeUserFromRoles';

/**
 * Bans a user from a room when triggered by federation or other external events.
 * Executes only the necessary database operations, with no callbacks, to prevent
 * propagation loops during external event processing.
 * `byUser` must be the Rocket.Chat user who initiated the ban (local record).
 */
export const performUserBan = async function (room: IRoom, user: IUser, byUser: IUser): Promise<void> {
	const subscription = await Subscriptions.findOneByRoomIdAndUserId(room._id, user._id);
	if (!subscription) {
		return;
	}

	if (!user.username) {
		throw new Error('User must have a username to be banned from the room');
	}

	// Already banned — nothing to do
	if (isBannedSubscription(subscription)) {
		return;
	}

	// Set subscription status to BANNED (keeps the record, unlike kick which deletes it)
	await Subscriptions.banByRoomIdAndUserId(room._id, user._id);

	// Remove the room from the user's __rooms array so they don't appear in member listings
	await Users.removeRoomByUserId(user._id, room._id);

	// Decrement the room's user count
	await Rooms.incUsersCountById(room._id, -1);

	// Remove room-scoped roles (moderator, owner, leader)
	if (['c', 'p'].includes(room.t)) {

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Set a username for the user first (admin UI or users.update), then ban.
  2. Fix the LDAP/SAML/import flow that produced a username-less user.
  3. Guard the ban action on username presence in callers.

Example fix

// before
await banUserFromRoom(rid, user, byUser); // throws if user.username is empty

// after
if (user.username) {
  await banUserFromRoom(rid, user, byUser);
} else {
  // set a username first, or reject the record
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (!user.username) {
  // set a username (users.update) or reject the record before banning
}

Type guard

const hasUsername = (u: IUser): u is IUser & { username: string } =>
  typeof u.username === 'string' && u.username.trim().length > 0;

if (hasUsername(user)) {
  await banUserFromRoom(rid, user, byUser);
}

Try / catch

try {
  await performUserBan(room, user, byUser);
} catch (error: any) {
  if (error?.message === 'User must have a username to be banned from the room') {
    // repair the user record (set username), then retry once
  }
}

Prevention

When it happens

Trigger: banUserFromRoom / performUserBan called for a user whose username is unset or empty: incomplete imports, SAML/LDAP users missing the username mapping, or placeholder federation users.

Common situations: LDAP/SAML field mapping not producing a username; users imported without usernames; moderation UI allowing actions on incomplete records.

Related errors


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