RocketChat/Rocket.Chat · error · Meteor.Error

error-room-is-not-closed

error-room-is-not-closed

Error message

error-room-is-not-closed

What it means

The last guard in removeOmnichannelRoom: open omnichannel rooms cannot be removed, so room.open === true raises Meteor.Error('error-room-is-not-closed'). The conversation must be closed before its room data is deleted.

Source

Thrown at apps/meteor/server/lib/omnichannel/rooms.ts:283

	callbacks.runAsync('livechat:afterReturnRoomAsInquiry', { room });

	return true;
}

export async function removeOmnichannelRoom(rid: string) {
	livechatLogger.debug({ msg: 'Deleting room', roomId: rid });
	check(rid, String);
	const room = await LivechatRooms.findOneById(rid);
	if (!room) {
		throw new Meteor.Error('error-invalid-room', 'Invalid room');
	}

	if (!isOmnichannelRoom(room)) {
		throw new Meteor.Error('error-this-is-not-a-livechat-room');
	}

	if (room.open) {
		throw new Meteor.Error('error-room-is-not-closed');
	}

	const inquiry = await LivechatInquiry.findOneByRoomId(rid);

	const result = await Promise.allSettled([
		Messages.removeByRoomId(rid),
		ReadReceipts.removeByRoomId(rid),
		Subscriptions.removeByRoomId(rid, {
			async onTrash(doc) {
				void notifyOnSubscriptionChanged(doc, 'removed');
			},
		}),
		LivechatInquiry.removeByRoomId(rid),
		LivechatRooms.removeById(rid),
		ReadReceiptsArchive.removeByRoomId(rid),
	]);

	if (result[3]?.status === 'fulfilled' && result[3].value?.deletedCount && inquiry) {

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Close the conversation first (omnichannel close flow/API), then call removeOmnichannelRoom.
  2. Verify room.open === false immediately before deleting.
  3. If a close already ran, re-fetch the room to see the updated state before retrying.

Example fix

// before
await removeOmnichannelRoom(rid); // room still open -> error-room-is-not-closed

// after
const room = await LivechatRooms.findOneById(rid);
if (room?.open) {
	await closeOmnichannelRoom({ room, user: closingUser });
}
await removeOmnichannelRoom(rid);
Defensive patterns

Strategy: validation

Validate before calling

const room = await LivechatRooms.findOneById(rid, { projection: { _id: 1, open: 1 } });
if (room?.open) {
	await closeOmnichannelRoom({ room, user: closingUser });
}
await removeOmnichannelRoom(rid);

Type guard

const isClosableForRemoval = (room: Pick<IOmnichannelRoom, 'open'>): boolean => room.open !== true;

Try / catch

try {
	await removeOmnichannelRoom(rid);
} catch (err) {
	if (err instanceof Meteor.Error && err.error === 'error-room-is-not-closed') {
		// close the conversation first, verify open === false, then retry once
		return;
	}
	throw err;
}

Prevention

When it happens

Trigger: Attempting to delete an omnichannel room whose conversation is still open — e.g., automation deletes rooms right after creation, or the close call raced/failed silently before the delete.

Common situations: Cleanup scripts that delete conversation rooms without closing them first; a preceding close request failed but the error was swallowed; UI shows the room as closed while the server still has open=true.

Related errors


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