RocketChat/Rocket.Chat · error · Error

error-user-not-banned

error-user-not-banned

Error message

error-user-not-banned

What it means

After finding the subscription, executeUnbanUserFromRoom checks isBannedSubscription(subscription). If the subscription exists but is neither an invite nor banned (ls/open like a normal member), unban makes no sense and the flow throws Error 'error-user-not-banned'.

Source

Thrown at apps/meteor/server/lib/rooms/executeUnbanUserFromRoom.ts:36

	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;
	}

	// if the subscription exists and is not an invite and not banned
	if (!isBannedSubscription(subscription)) {
		throw new Error('error-user-not-banned');
	}

	// Remove the subscription entirely — the user is no longer banned but also not a member.
	// Room count and __rooms were already adjusted during ban, so we only delete the document.
	await Subscriptions.removeById(subscription._id);

	await Message.saveSystemMessage('user-unbanned', rid, user.username, user, {
		u: { _id: byUser._id, username: byUser.username },
	});

	void notifyOnSubscriptionChanged(subscription, 'removed');
	void notifyOnRoomChangedById(rid);

	const inviterUser = await Users.findOneById(byUser._id);
	if (inviterUser) {
		await afterUnbanFromRoomCallback.run({ unbannedUser: user, userWhoUnbanned: inviterUser }, room);
	}
};

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Pre-check isBannedSubscription before unbanning; skip when the user is not banned
  2. Refresh moderation state after concurrent unbans so stale UIs do not resubmit
  3. Serialize ban/unban moderation actions per (rid, userId) to avoid races

Example fix

// before
Meteor.call('unbanUserFromRoom', rid, userId); // may throw error-user-not-banned

// after
const sub = await Subscriptions.findOneByRoomIdAndUserId(rid, userId);
if (!sub || (!isBannedSubscription(sub) && !isInviteSubscription(sub))) return;
Meteor.call('unbanUserFromRoom', rid, userId);
Defensive patterns

Strategy: validation

Validate before calling

const sub = await Subscriptions.findOneByRoomIdAndUserId(rid, user._id);
if (sub && !isInviteSubscription(sub) && !isBannedSubscription(sub)) {
  // user is a normal active member — nothing to unban
  return;
}
await executeUnbanUserFromRoom(rid, user, byUser);

Type guard

import { isBannedSubscription } from '@rocket.chat/core-typings';
// subscription has banned/anchor state -> unban is meaningful

Try / catch

try {
  await executeUnbanUserFromRoom(rid, user, byUser);
} catch (err) {
  if (err instanceof Error && err.message === 'error-user-not-banned') {
    // stale UI state — refresh the banned user list
  }
  throw err;
}

Prevention

When it happens

Trigger: Unbanning a user who is an active member (subscription without ban status), or whose ban marker was cleared by a concurrent unban. The isInviteSubscription branch handled earlier returns without error; only non-invite, non-banned subscriptions reach this throw.

Common situations: Stale moderation UI listing a user as banned after another moderator already unbanned them; permission misconfiguration letting unauthenticated flows call unban; events duplicated by federation or apps.

Related errors


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