RocketChat/Rocket.Chat · warning · Error

error-room-onHold

error-room-onHold

Error message

error-room-onHold

What it means

The omnichannel transfer() helper refuses to transfer a room whose onHold flag is set, throwing Error('error-room-onHold'). On-hold rooms are conversations paused waiting for the visitor to return; transferring them would break the hold semantics, so they must be resumed or closed first.

Source

Thrown at apps/meteor/server/lib/omnichannel/transfer.ts:69

	const { _id, username, name } = user;
	for await (const room of LivechatRooms.findOpenByAgent(userId)) {
		const guest = await LivechatVisitors.findOneEnabledById(room.v._id);
		if (!guest) {
			continue;
		}

		const transferredBy = normalizeTransferredByData({ _id, username, name }, room);
		await transfer(room, guest, {
			transferredBy,
			departmentId: guest.department,
		});
	}
}

export async function transfer(room: IOmnichannelRoom, guest: ILivechatVisitor, transferData: TransferData) {
	livechatLogger.debug({ msg: 'Transferring room', roomId: room._id, transferredBy: transferData?.transferredBy?._id });
	if (room.onHold) {
		throw new Error('error-room-onHold');
	}

	if (transferData.departmentId) {
		const department = await LivechatDepartment.findOneById<Pick<ILivechatDepartment, 'name' | '_id'>>(transferData.departmentId, {
			projection: { name: 1 },
		});
		if (!department) {
			throw new Error('error-invalid-department');
		}

		transferData.department = department;
		livechatLogger.debug({ msg: 'Transferring room to department', roomId: room._id, departmentId: transferData.department?._id });
	}

	return RoutingManager.transferRoom(room, guest, transferData);
}

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Resume the room first (take it off hold / send a message so it becomes active again), then transfer
  2. Or close the on-hold conversation instead of transferring it if it's stale
  3. In transfer UIs, detect room.onHold and offer resume-or-close instead of transfer
  4. Audit automation that transfers rooms to skip onHold === true rooms

Example fix

// before
await transfer(room, guest, transferData); // throws error-room-onHold

// after
if (room.onHold) {
  throw new Meteor.Error('error-room-onHold', 'Resume the room before transferring it');
}
await transfer(room, guest, transferData);
Defensive patterns

Strategy: validation

Validate before calling

import { LivechatRooms } from '@rocket.chat/models';

const room = await LivechatRooms.findOneById(rid, { projection: { onHold: 1 } });
if (room?.onHold) {
  throw new Meteor.Error('error-room-onHold', 'Resume the room before transferring');
}
await transfer(room, guest, transferData);

Type guard

function isTransferableRoom(v: { onHold?: boolean } | null | undefined): boolean {
  return !!v && v.onHold !== true;
}

Try / catch

try {
  await transfer(room, guest, transferData);
} catch (err) {
  if (err instanceof Error && err.message === 'error-room-onHold') {
    // offer resume-then-transfer or close instead of a raw error
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Calling the room transfer flow (agent 'transfer'/'return' action, or the corresponding livechat transfer method/API) for a room currently on hold — typically a chat put on hold by the visitor not responding (on-hold after inactivity) that an agent then tries to transfer to another department or agent.

Common situations: Agents trying to move stale on-hold chats to another queue, automated transfer rules (e.g. business-hours based) firing against held rooms, or on-hold rooms left in the list that operators attempt to reassign.

Related errors


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