RocketChat/Rocket.Chat · error · Meteor.Error
error-invalid-room
error-invalid-room
Error message
Invalid room
What it means
removeRoomOwner begins with Rooms.findOneById(rid, ...) and throws error-invalid-room when no document matches. As with the moderator variant, this precedes permission checks, so a nonexistent, deleted, or malformed room ID always produces it. rid must be the room's _id.
Solutions
- Look the room up first (client Rooms collection or GET /api/v1/rooms.info?roomId=) and pass the returned _id
- Reload the room context when the call fails so the UI drops dead references
- Always pass _id, never name, for rid
Example fix
// before
await Meteor.callAsync('removeRoomOwner', roomName, userId);
// after
const room = Rooms.findOne({ name: roomName });
if (!room) throw new Error('room gone');
await Meteor.callAsync('removeRoomOwner', room._id, userId); Defensive patterns
Strategy: validation
Validate before calling
const room = Rooms.findOne({ _id: rid });
if (!room) {
// stale rid: refetch rooms and abort
} Type guard
const isExistingRoom = (rid: string | undefined): rid is string =>
typeof rid === 'string' && rid.length > 0 && Rooms.findOne({ _id: rid }) != null; Try / catch
try {
await Meteor.callAsync('removeRoomOwner', rid, userId);
} catch (err) {
if (err instanceof Meteor.Error && err.error === 'error-invalid-room') {
// room gone: refresh UI, do not retry same rid
}
} Prevention
- Resolve rid via lookup; never synthesize from names
- Drop dead room references after delete events
- Validate IDs before method calls
When it happens
Trigger: Passing a room name instead of its _id; the room was deleted before the demotion call executed; truncated or mistyped ID string.
Common situations: Stale channel settings page after the channel was deleted; environment-to-environment data copies with different IDs; automation composing rid from slugs.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/0339404539600a60.
Report an issue: GitHub.
Appendix: source
Thrown at apps/meteor/server/meteor-methods/rooms/removeRoomOwner.ts:28
import { methodDeprecationLogger } from '../../lib/deprecationWarningLogger';
import { notifyOnSubscriptionChangedById } from '../../lib/notifyListener';
import { syncRoomRolePriorityForUserAndRoom } from '../../lib/roles/syncRoomRolePriority';
import { settings } from '../../settings';
declare module '@rocket.chat/ddp-client' {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface ServerMethods {
removeRoomOwner(rid: string, userId: string): boolean;
}
}
export const removeRoomOwner = async (fromUserId: string, rid: string, userId: string): 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: 'removeRoomOwner',
});
}
if (!(await hasPermissionAsync(fromUserId, 'set-owner', rid)) && !isRoomFederated(room)) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', {
method: 'removeRoomOwner',
});
}
const user = await Users.findOneById(userId);
if (!user?.username) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
method: 'removeRoomOwner',
});
}
const subscription = await Subscriptions.findOneByRoomIdAndUserId(rid, user._id);View on GitHub (pinned to b2c16d5842)