{"record":{"id":"4d5ff0436477cf41","repo":"RocketChat/Rocket.Chat","slug":"error-room-onhold-4d5ff0","errorCode":"error-room-onHold","errorMessage":"error-room-onHold","messagePattern":"error-room-onHold","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"apps/meteor/server/lib/omnichannel/transfer.ts","lineNumber":69,"sourceCode":"\tconst { _id, username, name } = user;\n\tfor await (const room of LivechatRooms.findOpenByAgent(userId)) {\n\t\tconst guest = await LivechatVisitors.findOneEnabledById(room.v._id);\n\t\tif (!guest) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst transferredBy = normalizeTransferredByData({ _id, username, name }, room);\n\t\tawait transfer(room, guest, {\n\t\t\ttransferredBy,\n\t\t\tdepartmentId: guest.department,\n\t\t});\n\t}\n}\n\nexport async function transfer(room: IOmnichannelRoom, guest: ILivechatVisitor, transferData: TransferData) {\n\tlivechatLogger.debug({ msg: 'Transferring room', roomId: room._id, transferredBy: transferData?.transferredBy?._id });\n\tif (room.onHold) {\n\t\tthrow new Error('error-room-onHold');\n\t}\n\n\tif (transferData.departmentId) {\n\t\tconst department = await LivechatDepartment.findOneById<Pick<ILivechatDepartment, 'name' | '_id'>>(transferData.departmentId, {\n\t\t\tprojection: { name: 1 },\n\t\t});\n\t\tif (!department) {\n\t\t\tthrow new Error('error-invalid-department');\n\t\t}\n\n\t\ttransferData.department = department;\n\t\tlivechatLogger.debug({ msg: 'Transferring room to department', roomId: room._id, departmentId: transferData.department?._id });\n\t}\n\n\treturn RoutingManager.transferRoom(room, guest, transferData);\n}\n","sourceCodeStart":51,"sourceCodeEnd":86,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/omnichannel/transfer.ts#L51-L86","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Resume the room first (take it off hold / send a message so it becomes active again), then transfer","Or close the on-hold conversation instead of transferring it if it's stale","In transfer UIs, detect room.onHold and offer resume-or-close instead of transfer","Audit automation that transfers rooms to skip onHold === true rooms"],"exampleFix":"// before\nawait transfer(room, guest, transferData); // throws error-room-onHold\n\n// after\nif (room.onHold) {\n  throw new Meteor.Error('error-room-onHold', 'Resume the room before transferring it');\n}\nawait transfer(room, guest, transferData);","handlingStrategy":"validation","validationCode":"import { LivechatRooms } from '@rocket.chat/models';\n\nconst room = await LivechatRooms.findOneById(rid, { projection: { onHold: 1 } });\nif (room?.onHold) {\n  throw new Meteor.Error('error-room-onHold', 'Resume the room before transferring');\n}\nawait transfer(room, guest, transferData);","typeGuard":"function isTransferableRoom(v: { onHold?: boolean } | null | undefined): boolean {\n  return !!v && v.onHold !== true;\n}","tryCatchPattern":"try {\n  await transfer(room, guest, transferData);\n} catch (err) {\n  if (err instanceof Error && err.message === 'error-room-onHold') {\n    // offer resume-then-transfer or close instead of a raw error\n  } else {\n    throw err;\n  }\n}","preventionTips":["Check room.onHold in transfer UIs and offer resume/close alternatives","Make automation (business hours, auto-transfer rules) skip on-hold rooms","Close stale on-hold conversations on a schedule instead of transferring them"],"tags":["omnichannel","transfer","on-hold","room-state"],"backgroundTag":"invalid-room-state","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}