RocketChat/Rocket.Chat · error · Meteor.Error
error-invalid-room
error-invalid-room
Error message
Invalid room
What it means
addRoomModerator begins by loading the room with Rooms.findOneById (projection t/federated/federation); a null result throws error-invalid-room. The room lookup precedes both the set-moderator permission check and the federation checks, so a bad rid fails before anything else. Note addRoomModerator validates the room first, unlike addRoomLeader which checks permission first.
Source
Thrown at apps/meteor/server/meteor-methods/rooms/addRoomModerator.ts:30
import { notifyOnSubscriptionChangedById } from '../../lib/notifyListener';
import { syncRoomRolePriorityForUserAndRoom } from '../../lib/roles/syncRoomRolePriority';
import { isFederationEnabled, FederationMatrixInvalidConfigurationError } from '../../services/federation/utils';
import { settings } from '../../settings';
declare module '@rocket.chat/ddp-client' {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface ServerMethods {
addRoomModerator(rid: IRoom['_id'], userId: IUser['_id']): boolean;
}
}
export const addRoomModerator = async (fromUserId: IUser['_id'], rid: IRoom['_id'], userId: IUser['_id']): Promise<boolean> => {
check(rid, String);
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: 'addRoomModerator',
});
}
const isFederated = isRoomFederated(room);
if (!(await hasPermissionAsync(fromUserId, 'set-moderator', rid)) && !isFederated) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', {
method: 'addRoomModerator',
});
}
if (isFederated && !isFederationEnabled()) {
throw new FederationMatrixInvalidConfigurationError('unable to change room owners');
}
const user = await Users.findOneById(userId);
View on GitHub (pinned to b2c16d5842)
Solutions
- Verify the room exists (GET /v1/channels.info or /v1/groups.info) before calling.
- Catch error-invalid-room and refresh the room data the UI operates on.
- Resolve rid from the room name at call time in automation.
Defensive patterns
Strategy: validation
Validate before calling
// validate the room id before the role change
const room = await fetch(`/api/v1/rooms.info?roomId=${rid}`, { headers }).then((r) => r.json());
if (!room.success) {
throw new Error(`Room ${rid} not found`);
} Try / catch
try {
await Meteor.callAsync('addRoomModerator', rid, userId);
} catch (e: any) {
if (e?.error === 'error-invalid-room') {
// stale rid: refresh room data, re-run with the current id
}
} Prevention
- Resolve rids at call time; never hardcode them in scripts.
- Invalidate cached room references on room-deleted events.
- Validate the rid format before any room-scoped call.
When it happens
Trigger: Passing a nonexistent or deleted rid; the room was removed between the client loading it and the role-change call; typos in scripted rids.
Common situations: Stale room selectors; deletion races during channel cleanup; scripts hardcoding ids from an old export.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/6b0eb8cec1a9c55e.
Report an issue: GitHub.