RocketChat/Rocket.Chat · error
room_is_blocked
Error message
room_is_blocked
What it means
When the room type's directives allow the BLOCK member action (direct messages do), validateRoomMessagePermissionsAsync throws room_is_blocked (plain Error) if the caller's subscription has blocked or blocker set — i.e. a DM where either participant blocked the other. Rocket.Chat stores that state on the DM subscription document.
Source
Thrown at apps/meteor/server/lib/authorization/canSendMessage.ts:38
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');
}
}
if (room.ro === true && !(await hasPermissionAsync('uid' in args ? args.uid : args._id, 'post-readonly', room._id))) {
// Unless the user was manually unmuted
if (args.username && !(room.unmuted || []).includes(args.username)) {
throw new Error("You can't send messages because the room is readonly.");
}
}
if (args.username && room?.muted?.includes(args.username)) {
throw new Error('You_have_been_muted');
}
}
// TODO: remove option uid and username and type
export async function canSendMessageAsync(
rid: IRoom['_id'],
user: { uid: IUser['_id']; username: IUser['username']; type: IUser['type'] } | IUser,View on GitHub (pinned to b2c16d5842)
Solutions
- Unblock the user (user card -> unblock), which clears the blocked/blocker flags on the DM subscription
- Route the automation through a normal channel instead of the blocked DM
- Before sending programmatically, inspect the subscription (Subscriptions.findOneByRoomIdAndUserId with projection { blocked: 1, blocker: 1 }) and skip blocked DMs
Defensive patterns
Strategy: validation
Validate before calling
const sub = await Subscriptions.findOneByRoomIdAndUserId(rid, uid, { projection: { blocked: 1, blocker: 1 } });
if (sub?.blocked || sub?.blocker) {
// DM is blocked in one or both directions: skip the send and report 'blocked'
} Type guard
const isBlockedSubscription = (s: { blocked?: boolean; blocker?: boolean } | null | undefined): boolean =>
s?.blocked === true || s?.blocker === true; Try / catch
try {
await sendMessage(...);
} catch (e) {
if (e instanceof Error && e.message === 'room_is_blocked') {
// a participant blocked the other: do not retry; offer unblock or another channel
}
throw e;
} Prevention
- In DM automations, check the subscription flags before each send
- Prefer posting as a bot added to a shared channel over DMs
- Treat blocked DMs as a normal state, not an error, in bulk messaging jobs
When it happens
Trigger: sendMessage in a direct-message room where subscription.blocked (you blocked them) or subscription.blocker (they blocked you) is set — typically an automation reusing a stale DM rid after a block happened.
Common situations: A user blocks a contact and an old bot/automation still tries to DM them; both directions of a DM break for integrations when either side blocks; background jobs iterate all DMs without filtering blocked ones.
Related errors
- error-invalid-room
- error-invalid-room
- Invalid Api parameter provided, it must be a valid IApi obje
- Room with id ${transferData.room} not found
- Room not found
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/b7feea75aa318ca4.
Report an issue: GitHub.