RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-room

error-invalid-room

Error message

Invalid room

What it means

`setRoomKeyIDMethod` throws `error-invalid-room` when `canAccessRoomIdAsync(rid, userId)` returns false: the rid matches no room, or the user cannot access it (not a member of a private room, no access to that room type). Setting a room's E2E key id requires the caller to already have access to the room.

Source

Thrown at apps/meteor/server/meteor-methods/platform/setRoomKeyID.ts:19

import type { IRoom } from '@rocket.chat/core-typings';
import type { ServerMethods } from '@rocket.chat/ddp-client';
import { Rooms } from '@rocket.chat/models';
import { check } from 'meteor/check';
import { Meteor } from 'meteor/meteor';

import { canAccessRoomIdAsync } from '../../lib/authorization/canAccessRoom';
import { notifyOnRoomChanged } from '../../lib/notifyListener';

declare module '@rocket.chat/ddp-client' {
	// eslint-disable-next-line @typescript-eslint/naming-convention
	interface ServerMethods {
		'e2e.setRoomKeyID'(rid: IRoom['_id'], keyID: string): void;
	}
}

export const setRoomKeyIDMethod = async (userId: string, rid: IRoom['_id'], keyID: string): Promise<void> => {
	if (!(await canAccessRoomIdAsync(rid, userId))) {
		throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'e2e.setRoomKeyID' });
	}

	const room = await Rooms.setE2eKeyId(rid, keyID);

	if (!room) {
		throw new Meteor.Error('error-room-e2e-key-already-exists', 'E2E Key ID already exists', {
			method: 'e2e.setRoomKeyID',
		});
	}

	void notifyOnRoomChanged(room);
};

Meteor.methods<ServerMethods>({
	async 'e2e.setRoomKeyID'(rid, keyID) {
		check(rid, String);
		check(keyID, String);

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Verify the rid exists and the caller has a subscription to it before invoking
  2. For private rooms, join/be added first so the access check passes
  3. Refresh room data on the client before running E2E key operations

Example fix

// before
Meteor.call('e2e.setRoomKeyID', rid, keyID);
// after
const sub = Subscriptions.findOne({ rid });
if (!sub) {
  throw new Error('no access to room');
}
Meteor.call('e2e.setRoomKeyID', rid, keyID);
Defensive patterns

Strategy: validation

Validate before calling

const sub = Subscriptions.findOne({ rid });
if (!sub) {
  throw new Error('user cannot access room');
}
Meteor.call('e2e.setRoomKeyID', rid, keyID);

Type guard

const canSubmitRoomKey = (rid: string | undefined, sub: ISubscription | undefined): rid is string =>
  typeof rid === 'string' && rid.length > 0 && !!sub;

Try / catch

try {
  await Meteor.callAsync('e2e.setRoomKeyID', rid, keyID);
} catch (e) {
  if (e instanceof Meteor.Error && e.error === 'error-invalid-room') {
    // rid wrong or no access: refresh room data, verify membership, do not blind-retry
  }
}

Prevention

When it happens

Trigger: `Meteor.call('e2e.setRoomKeyID', rid, keyID)` with a typo'd or deleted rid, or a private room the caller never joined (access check fails even though the room exists).

Common situations: Client state desync — rid taken from a stale cache; room deleted between load and key submission; guest/bot accounts without membership attempting key exchange.

Related errors


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