RocketChat/Rocket.Chat · error

error-invalid-room

Error message

error-invalid-room

What it means

validateRoomMessagePermissionsAsync throws error-invalid-room (plain Error) when the room argument passed in is null. This differs from the lookup variant in canSendMessageAsync: here the caller already resolved the room and passed null, so the failure is an upstream lookup that found nothing.

Source

Thrown at apps/meteor/server/lib/authorization/canSendMessage.ts:23

import { hasPermissionAsync } from './hasPermission';
import { RoomMemberActions } from '../../../definition/IRoomTypeConfig';
import { roomCoordinator } from '../rooms/roomCoordinator';

const subscriptionOptions = {
	projection: {
		blocked: 1,
		blocker: 1,
	},
};

// TODO: remove option uid and username and type
export async function validateRoomMessagePermissionsAsync(
	room: IRoom | null,
	args: { uid: IUser['_id']; username: IUser['username']; type: IUser['type'] } | IUser,
	extraData?: Record<string, any>,
): Promise<void> {
	if (!room) {
		throw new Error('error-invalid-room');
	}

	if (room.archived) {
		throw new Error('room_is_archived');
	}
	if (args.type !== 'app' && !(await canAccessRoomAsync(room, 'uid' in args ? { _id: args.uid } : args, extraData))) {
		throw new Error('error-not-allowed');
	}

	if (
		await roomCoordinator.getRoomDirectives(room.t).allowMemberAction(room, RoomMemberActions.BLOCK, 'uid' in args ? args.uid : args._id)
	) {
		const subscription = await Subscriptions.findOneByRoomIdAndUserId(room._id, 'uid' in args ? args.uid : args._id, subscriptionOptions);
		if (subscription && (subscription.blocked || subscription.blocker)) {
			throw new Error('room_is_blocked');
		}
	}

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Check the room lookup result before calling and fail fast with a clear error if missing
  2. Prefer canSendMessageAsync(rid, user) which performs lookup plus validation in one step
  3. Validate rid existence at the trust boundary (API handler/webhook) before touching authorization

Example fix

// before
const room = await Rooms.findOneById(rid);
await validateRoomMessagePermissionsAsync(room, user); // room may be null -> error-invalid-room

// after
const room = await Rooms.findOneById(rid);
if (!room) throw new Meteor.Error('error-invalid-room', `Room ${rid} not found`);
await validateRoomMessagePermissionsAsync(room, user);
Defensive patterns

Strategy: type-guard

Validate before calling

const room = await Rooms.findOneById(rid);
if (!isExistingRoom(room)) {
  throw new Meteor.Error('error-invalid-room', `Room not found: ${rid}`);
}
await validateRoomMessagePermissionsAsync(room, user, extraData);

Type guard

const isExistingRoom = (room: IRoom | null | undefined): room is IRoom => !!room?._id;

Prevention

When it happens

Trigger: Calling validateRoomMessagePermissionsAsync(null, user, ...) — code that did Rooms.findOneById(rid) without checking the result, or a hook/webhook passing an absent room object.

Common situations: Integrations forwarding to rooms that were deleted; stale or mistyped room ids in configuration; code refactored to call the permissions function before validating its own lookup.

Related errors


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