RocketChat/Rocket.Chat · error · Meteor.Error

error-not-allowed

error-not-allowed

Error message

Not allowed

What it means

getRoomById maps a missing room to 'error-not-allowed' instead of a dedicated not-found code: Rooms.findOneById(rid) returned null, so no room document exists with that _id. Because the same code is reused at line 34 for real access denial, this error alone cannot distinguish 'no such room' from 'no access'.

Source

Thrown at apps/meteor/server/meteor-methods/rooms/getRoomById.ts:29

	// eslint-disable-next-line @typescript-eslint/naming-convention
	interface ServerMethods {
		getRoomById(rid: IRoom['_id']): IRoom;
	}
}

Meteor.methods<ServerMethods>({
	async getRoomById(rid) {
		check(rid, String);
		const userId = Meteor.userId();
		if (!userId) {
			throw new Meteor.Error('error-invalid-user', 'Invalid user', {
				method: 'getRoomNameById',
			});
		}

		const room = await Rooms.findOneById(rid);
		if (room == null) {
			throw new Meteor.Error('error-not-allowed', 'Not allowed', {
				method: 'getRoomNameById',
			});
		}
		if (!(await canAccessRoomAsync(room, (await Meteor.userAsync()) as IUser))) {
			throw new Meteor.Error('error-not-allowed', 'Not allowed', {
				method: 'getRoomById',
			});
		}
		return room;
	},
});

DDPRateLimiter.addRule(
	{
		type: 'method',
		name: 'getRoomById',
		userId() {
			return true;

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Re-fetch the rid from a live source (rooms subscription or the /channel/<name> routing) instead of a cached value
  2. Confirm the room exists on the server: db.rooms.findOne({_id: rid})
  3. Handle 'error-not-allowed' as either not-found or no-access: refresh the room list and drop the stale reference
  4. Prefer GET /api/v1/rooms.info?roomId=... which returns a distinguishable not-found response

Example fix

// before - rid may be stale after the room was deleted
const room = await Meteor.callAsync('getRoomById', rid);

// after - treat failure as invalid reference and resync
try {
  const room = await Meteor.callAsync('getRoomById', rid);
} catch (e) {
  if (e?.error === 'error-not-allowed') {
    await refreshRoomList(); // drop stale rid, reload from server truth
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// verify shape and liveness of the rid before the call
if (typeof rid !== 'string' || rid.length !== 17) throw new Error('malformed room id');
const known = RoomManager.getOpenedRoomByRid(rid); // client room cache
if (!known) throw new Error('unknown room');

Try / catch

try {
  const room = await Meteor.callAsync('getRoomById', rid);
} catch (e) {
  if (e instanceof Meteor.Error && e.error === 'error-not-allowed') {
    // ambiguous: room missing OR access denied - resync room list and drop the rid
    await refreshRooms();
  }
}

Prevention

When it happens

Trigger: Meteor.call('getRoomById', rid) with a nonexistent, already-deleted, or mistyped room id. Since check(rid, String) passes for any string, this path specifically means a well-formed id with no matching Rooms document.

Common situations: Stale rid persisted in localStorage or a URL from a room that was later deleted; race where the rooms subscription updates after a deletion but UI code still holds the old rid; passing a subscription id or message id where a room id was expected.

Related errors


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