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

  1. Unblock the user (user card -> unblock), which clears the blocked/blocker flags on the DM subscription
  2. Route the automation through a normal channel instead of the blocked DM
  3. 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

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


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