{"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/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/ee/server/local-services/omnichannel.internalService.ts#L36-L72","documentation":"With the setting 'Livechat_allow_manual_on_hold_upon_agent_engagement_only' enabled, on-hold is only allowed when the agent side engaged last: canAgentPlaceOnHold is computed as !room.lastMessage?.token (visitor messages carry a token), and Error('error-cannot-place-chat-on-hold') is thrown when the setting is on and the last message was the visitor's. With the setting disabled, this error cannot occur because canPlaceChatOnHold short-circuits to true.","triggerScenarios":"An agent clicks on-hold immediately after the visitor's message while the engagement-only setting is enabled; or queue automation tries to hold unanswered chats - with the setting on, any hold where the visitor sent the last message fails by design.","commonSituations":"The setting was enabled as policy after workflows were built assuming hold-anytime semantics; bots/auto-replies counted as agent engagement when they were not; agents unaware the last speaker must be the agent.","solutions":["Have the agent send a message before holding - that satisfies the 'agent engagement' precondition (last message without a token).","If the workflow must hold right after visitor messages, disable 'Livechat_allow_manual_on_hold_upon_agent_engagement_only' in Livechat settings.","Adjust automation to only attempt holds when room.lastMessage has no token (agent spoke last)."],"exampleFix":"// before\nawait service.placeRoomOnHold(room, comment, user); // last message was the visitor's; setting on -> throws\n\n// after\nconst restricted = settings.get<boolean>('Livechat_allow_manual_on_hold_upon_agent_engagement_only');\nif (restricted && room.lastMessage?.token) {\n\tawait sendMessage(agent, rid, 'Placing this chat on hold'); // engage first\n}\nawait service.placeRoomOnHold(refetchedRoom, comment, user);","handlingStrategy":"validation","validationCode":"const restricted = settings.get<boolean>('Livechat_allow_manual_on_hold_upon_agent_engagement_only');\nconst agentEngagedLast = !room.lastMessage?.token; // visitor messages carry a token\nif (restricted && !agentEngagedLast) {\n\t// agent must send a message first, or the setting must be off; do not call placeRoomOnHold yet\n}","typeGuard":null,"tryCatchPattern":"try {\n\tawait service.placeRoomOnHold(room, comment, user);\n} catch (e: any) {\n\tif (e?.message === 'error-cannot-place-chat-on-hold') { surface('Send a message to the visitor before placing the chat on hold'); return; }\n\tthrow e;\n}","preventionTips":["Know the 'Livechat_allow_manual_on_hold_upon_agent_engagement_only' setting before building hold automation.","Only attempt holds when the last message lacks a token (agent side spoke last).","Update agent training/scripts: with the setting on, hold is an after-reply action."],"tags":["omnichannel","on-hold","settings","business-rules","enterprise"],"backgroundTag":"precondition-failed","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}