{"record":{"id":"bb8d2016aa5e0cb2","repo":"RocketChat/Rocket.Chat","slug":"error-cannot-place-chat-on-hold","errorCode":null,"errorMessage":"error-cannot-place-chat-on-hold","messagePattern":"error-cannot-place-chat-on-hold","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/ee/server/local-services/omnichannel.internalService.ts","lineNumber":54,"sourceCode":"\t\tthis.logger.debug({ msg: 'Attempting to place room on hold', roomId: room._id, userId: onHoldBy?._id });\n\n\t\tconst { _id: roomId } = room;\n\n\t\tif (!room || !isOmnichannelRoom(room)) {\n\t\t\tthrow new Error('error-invalid-room');\n\t\t}\n\t\tif (!room.open) {\n\t\t\tthrow new Error('error-room-already-closed');\n\t\t}\n\t\tif (room.onHold) {\n\t\t\tthrow new Error('error-room-is-already-on-hold');\n\t\t}\n\t\tconst restrictedOnHold = settings.get('Livechat_allow_manual_on_hold_upon_agent_engagement_only');\n\t\tconst canRoomBePlacedOnHold = !room.onHold;\n\t\tconst canAgentPlaceOnHold = !room.lastMessage?.token;\n\t\tconst canPlaceChatOnHold = canRoomBePlacedOnHold && (!restrictedOnHold || canAgentPlaceOnHold);\n\t\tif (!canPlaceChatOnHold) {\n\t\t\tthrow new Error('error-cannot-place-chat-on-hold');\n\t\t}\n\t\tif (!room.servedBy) {\n\t\t\tthrow new Error('error-unserved-rooms-cannot-be-placed-onhold');\n\t\t}\n\n\t\tconst [roomResult, subsResult] = await Promise.all([\n\t\t\tLivechatRooms.setOnHoldByRoomId(roomId),\n\t\t\tSubscriptions.setOnHoldByRoomId(roomId),\n\t\t\tMessage.saveSystemMessage<IOmnichannelSystemMessage>('omnichannel_placed_chat_on_hold', roomId, '', onHoldBy, { comment }),\n\t\t]);\n\n\t\tif (roomResult.modifiedCount) {\n\t\t\tvoid notifyOnRoomChangedById(roomId);\n\t\t}\n\n\t\tif (subsResult.modifiedCount) {\n\t\t\tvoid notifyOnSubscriptionChangedByRoomId(roomId);\n\t\t}","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/ee/server/local-services/omnichannel.internalService.ts#L36-L72","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait omnichannelService.placeRoomOnHold(room, comment, user);\n\n// after\nconst restricted = settings.get('Livechat_allow_manual_on_hold_upon_agent_engagement_only');\nif (restricted && room.lastMessage?.token) {\n  throw new Error('Reply to the visitor before placing the chat on hold');\n}\nawait omnichannelService.placeRoomOnHold(room, comment, user);","handlingStrategy":"validation","validationCode":"const restricted = settings.get('Livechat_allow_manual_on_hold_upon_agent_engagement_only');\nif (restricted && room.lastMessage?.token) {\n  throw new Error('Reply to the visitor before placing the chat on hold');\n}\nawait omnichannelService.placeRoomOnHold(room, comment, user);","typeGuard":"function lastMessageFromAgent(room: { lastMessage?: { token?: string } }): boolean {\n  return !room.lastMessage?.token;\n}","tryCatchPattern":"try {\n  await omnichannelService.placeRoomOnHold(room, comment, user);\n} catch (e) {\n  if (e instanceof Error && e.message === 'error-cannot-place-chat-on-hold') {\n    notifyUser('Send a reply before placing this chat on hold.');\n    return;\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["omnichannel","livechat","settings","business-rule"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}