RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-room

error-invalid-room

Error message

Invalid room

What it means

POST /api/v1/im.blockUser resolves the DM room via findDirectMessageRoom, then computes the other participant as room.uids.find((uid) => uid !== this.userId) (im.ts:~983). If no other uid exists the room is not a two-user DM from the caller's perspective - canonically the self-chat DM - so blocking is meaningless and error-invalid-room is thrown with details {method: 'im.blockUser'}. (findDirectMessageRoom throws the same code earlier when the room cannot be resolved at all.)

Solutions

  1. Guard before calling: skip rooms where room.uids does not contain some uid different from the current user
  2. Hide the block/unblock action entirely for self-chats in the UI
  3. If genuine two-user DMs also fail, audit the room documents' uids array (import or migration damage)

Example fix

// before
 await api.post('/api/v1/im.blockUser', { roomId, block: true }); // roomId is the self-DM

 // after
 const room = rooms.find((r) => r._id === roomId);
 const hasOtherUser = room?.uids?.some((uid) => uid !== currentUserId);
 if (!hasOtherUser) throw new Error('Cannot block: no other participant in this room');
 await api.post('/api/v1/im.blockUser', { roomId, block: true });
Defensive patterns

Strategy: validation

Validate before calling

const { ims } = await api.get('/api/v1/im.list');
const room = ims.find((r) => r._id === roomId);
const hasOtherParticipant = (uids: string[] | undefined, me: string) =>
  Array.isArray(uids) && uids.some((uid) => uid !== me);
if (!room || !hasOtherParticipant(room.uids, currentUserId)) {
  throw new Error('Room is not a two-user DM - block/unblock not applicable (self-chat or corrupt uids)');
}
await api.post('/api/v1/im.blockUser', { roomId, block: true });

Type guard

const isBlockableDM = (room: { _id: string; uids?: string[] }, currentUserId: string): room is { _id: string; uids: [string, string] } =>
  !!room.uids && room.uids.some((uid) => uid !== currentUserId);

Prevention

When it happens

Trigger: Calling im.blockUser on your own self-conversation (uids contains only your id, or only duplicates of it); room documents with missing/empty uids after a bad import; calling with a token for a user who is not a participant of the resolved room.

Common situations: Clients that render a block action for every room returned by im.list, including the self-DM; moderation scripts iterating all DMs of a user; data-corruption cases where uids was wiped.

Related errors


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

Appendix: source

Thrown at apps/meteor/server/api/v1/im.ts:991

		200: ajv.compile<void>({
			type: 'object',
			properties: {
				success: { type: 'boolean', enum: [true] },
			},
			required: ['success'],
			additionalProperties: false,
		}),
	},
} as const;

const dmBlockUserAction = <Path extends string>(_path: Path): TypedAction<typeof dmBlockUserEndpointsProps, Path> =>
	async function action() {
		const { roomId, block } = this.bodyParams;
		const { room } = await findDirectMessageRoom({ roomId }, this.userId);

		const blocked = room.uids?.find((uid) => uid !== this.userId);
		if (!blocked) {
			throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'im.blockUser' });
		}

		if (block) {
			await blockUserMethod(this.userId, { rid: room._id, blocked });
		} else {
			await unblockUserMethod(this.userId, { rid: room._id, blocked });
		}

		return API.v1.success();
	};

const dmEndpoints = API.v1
	.post('im.delete', dmDeleteEndpointsProps, dmDeleteAction('im.delete'))
	.post('dm.delete', dmDeleteEndpointsProps, dmDeleteAction('dm.delete'))
	.post('dm.close', dmCloseEndpointsProps, dmCloseAction('dm.close'))
	.post('im.close', dmCloseEndpointsProps, dmCloseAction('im.close'))
	.post('dm.create', dmCreateEndpointsProps, dmCreateAction('dm.create'))
	.post('im.create', dmCreateEndpointsProps, dmCreateAction('im.create'))

View on GitHub (pinned to e4b8178b20)