RocketChat/Rocket.Chat · error · Error
error-invalid-subscription
error-invalid-subscription
Error message
error-invalid-subscription
What it means
Before unbanning, the code looks up the target's subscription with Subscriptions.findOneByRoomIdAndUserId(rid, user._id). No subscription means the user is not associated with the room at all (banning removes the subscription differently in this design, so a banned user still has one), hence Error 'error-invalid-subscription'.
Source
Thrown at apps/meteor/server/lib/rooms/executeUnbanUserFromRoom.ts:20
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;
}
// if the subscription exists and is not an invite and not banned
if (!isBannedSubscription(subscription)) {
throw new Error('error-user-not-banned');
}
View on GitHub (pinned to b2c16d5842)
Solutions
- Treat as idempotent: pre-check the subscription (or catch the error) and report 'already unbanned'
- Disable the unban action after first submission to prevent duplicate requests
- Refresh the banned-users list from subscription state before offering unban
Example fix
// before
Meteor.call('unbanUserFromRoom', rid, userId);
// after
const sub = await Subscriptions.findOneByRoomIdAndUserId(rid, userId, { projection: { _id: 1 } });
if (!sub) return; // already unbanned / never a member
Meteor.call('unbanUserFromRoom', rid, userId); Defensive patterns
Strategy: validation
Validate before calling
const sub = await Subscriptions.findOneByRoomIdAndUserId(rid, user._id, { projections: { _id: 1 } });
if (!sub) {
// user already fully removed — treat as success (idempotent unban)
return;
}
await executeUnbanUserFromRoom(rid, user, byUser); Try / catch
try {
await executeUnbanUserFromRoom(rid, user, byUser);
} catch (err) {
if (err instanceof Error && err.message === 'error-invalid-subscription') {
// idempotent case — already unbanned/removed; no-op
}
throw err;
} Prevention
- Make unban actions idempotent by pre-checking subscription existence
- Disable the unban UI action immediately after success
- Debounce double-submit on moderation buttons
When it happens
Trigger: Unbanning a user who was never in the room, whose subscription was already removed (e.g. unban executed twice, or ban flow deleted the doc), or passing mismatched rid/user._id pairs.
Common situations: Double-click / duplicate unban requests racing after the first one removed state; UI dropdown showing stale banned-user lists after another admin already unbanned; federation events out of order.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/3e1d6a20d4bc7787.
Report an issue: GitHub.