RocketChat/Rocket.Chat · error · Error
error-unserved-rooms-cannot-be-placed-onhold
Error message
error-unserved-rooms-cannot-be-placed-onhold
What it means
Thrown by placeRoomOnHold when `room.servedBy` is falsy. A chat without a serving agent cannot be put on hold because there is no agent relationship to suspend. This is the last guard before the room/subscription update.
Source
Thrown at apps/meteor/ee/server/local-services/omnichannel.internalService.ts:57
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);
}
await callbacks.run('livechat:afterOnHold', room);
}View on GitHub (pinned to f9d3ec372b)
Solutions
- Only expose Hold for rooms with an active servedBy; gate the UI on room.servedBy being truthy.
- Re-fetch the room to confirm it is still assigned before calling placeRoomOnHold.
- If the chat is queued, route it through the assignment flow first, not the hold flow.
Example fix
// before
await omnichannelService.placeRoomOnHold(room, comment, user);
// after
if (!room.servedBy) {
throw new Error('Room has no serving agent; cannot place on hold');
}
await omnichannelService.placeRoomOnHold(room, comment, user); Defensive patterns
Strategy: validation
Validate before calling
if (!room.servedBy) {
throw new Error('Room has no serving agent; cannot place on hold');
}
await omnichannelService.placeRoomOnHold(room, comment, user); Type guard
function hasServingAgent(room: unknown): boolean {
return !!room && !!(room as any).servedBy;
} Try / catch
try {
await omnichannelService.placeRoomOnHold(room, comment, user);
} catch (e) {
if (e instanceof Error && e.message === 'error-unserved-rooms-cannot-be-placed-onhold') {
notifyUser('This chat has no assigned agent.');
return;
}
throw e;
} Prevention
- Only show Hold for rooms with an active servedBy.
- Re-fetch the room to confirm assignment before the call.
- Route unassigned chats through assignment, not hold.
When it happens
Trigger: Calling placeRoomOnHold on a room whose servedBy field is null/undefined: an unclaimed queued inquiry, a room whose agent was removed, or a chat returned to the queue.
Common situations: Trying to hold a chat that is still queued (no agent assigned); the servedBy relation was cleared by a transfer/abandon flow before the hold call.
Related errors
- error-room-already-closed
- error-room-is-already-on-hold
- This_conversation_is_already_closed
- error-room-not-on-hold
- error-room-not-served
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/c5d83dfa77dc76ac.
Report an issue: GitHub.