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
- Guard before calling: skip rooms where room.uids does not contain some uid different from the current user
- Hide the block/unblock action entirely for self-chats in the UI
- 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
- Filter self-chat rooms out of any UI that offers block/unblock
- Skip rooms whose uids array is missing or contains only the current user before calling im.blockUser/im.unblockUser
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
- error-roomid-param-not-provided
- error-creator-not-in-room
- error-endpoint-disabled
- error-id-param-not-provided
- error-invalid-arguments
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)