RocketChat/Rocket.Chat · error · Error
error-cannot-place-chat-on-hold
Error message
error-cannot-place-chat-on-hold
What it means
Thrown by placeRoomOnHold when `canPlaceChatOnHold` is false. That value is `canRoomBePlacedOnHold && (!restrictedOnHold || canAgentPlaceOnHold)` where restrictedOnHold comes from setting 'Livechat_allow_manual_on_hold_upon_agent_engagement_only' and canAgentPlaceOnHold is `!room.lastMessage?.token` (true when the last message was sent by an agent, false when the visitor sent it). So the error fires when the restrict-on-hold setting is enabled AND the last message in the room came from the visitor.
Source
Thrown at apps/meteor/ee/server/local-services/omnichannel.internalService.ts:54
this.logger.debug({ msg: 'Attempting to place room on hold', roomId: room._id, userId: onHoldBy?._id });
const { _id: roomId } = room;
if (!room || !isOmnichannelRoom(room)) {
throw new Error('error-invalid-room');
}
if (!room.open) {
throw new Error('error-room-already-closed');
}
if (room.onHold) {
throw new Error('error-room-is-already-on-hold');
}
const restrictedOnHold = settings.get('Livechat_allow_manual_on_hold_upon_agent_engagement_only');
const canRoomBePlacedOnHold = !room.onHold;
const canAgentPlaceOnHold = !room.lastMessage?.token;
const canPlaceChatOnHold = canRoomBePlacedOnHold && (!restrictedOnHold || canAgentPlaceOnHold);
if (!canPlaceChatOnHold) {
throw new Error('error-cannot-place-chat-on-hold');
}
if (!room.servedBy) {
throw new Error('error-unserved-rooms-cannot-be-placed-onhold');
}
const [roomResult, subsResult] = await Promise.all([
LivechatRooms.setOnHoldByRoomId(roomId),
Subscriptions.setOnHoldByRoomId(roomId),
Message.saveSystemMessage<IOmnichannelSystemMessage>('omnichannel_placed_chat_on_hold', roomId, '', onHoldBy, { comment }),
]);
if (roomResult.modifiedCount) {
void notifyOnRoomChangedById(roomId);
}
if (subsResult.modifiedCount) {
void notifyOnSubscriptionChangedByRoomId(roomId);
}View on GitHub (pinned to f9d3ec372b)
Solutions
- Have the agent send at least one message in the room before attempting to hold it.
- If business rules allow holding without engagement, disable 'Livechat_allow_manual_on_hold_upon_agent_engagement_only' in Livechat settings.
- In the UI, only enable the Hold button when the last message is from an agent (room.lastMessage?.token is falsy) under that setting.
Example fix
// before
await omnichannelService.placeRoomOnHold(room, comment, user);
// after
const restricted = settings.get('Livechat_allow_manual_on_hold_upon_agent_engagement_only');
if (restricted && room.lastMessage?.token) {
throw new Error('Reply to the visitor before placing the chat on hold');
}
await omnichannelService.placeRoomOnHold(room, comment, user); Defensive patterns
Strategy: validation
Validate before calling
const restricted = settings.get('Livechat_allow_manual_on_hold_upon_agent_engagement_only');
if (restricted && room.lastMessage?.token) {
throw new Error('Reply to the visitor before placing the chat on hold');
}
await omnichannelService.placeRoomOnHold(room, comment, user); Type guard
function lastMessageFromAgent(room: { lastMessage?: { token?: string } }): boolean {
return !room.lastMessage?.token;
} Try / catch
try {
await omnichannelService.placeRoomOnHold(room, comment, user);
} catch (e) {
if (e instanceof Error && e.message === 'error-cannot-place-chat-on-hold') {
notifyUser('Send a reply before placing this chat on hold.');
return;
}
throw e;
} Prevention
- Only enable Hold when the last message is from an agent under the engagement-only setting.
- Document the 'Livechat_allow_manual_on_hold_upon_agent_engagement_only' effect for agents.
- Train agents to reply before holding when the setting is active.
When it happens
Trigger: Settings has 'Livechat_allow_manual_on_hold_upon_agent_engagement_only' enabled, and the agent tries to place the chat on hold before sending any reply (lastMessage.token is set = visitor message). The guard enforces 'agent must have engaged' before a manual hold.
Common situations: Admin enables the engagement-only hold setting; agent opens a chat and immediately hits Hold without replying; visitor's message is the latest in the thread.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/bb8d2016aa5e0cb2.
Report an issue: GitHub.