{"record":{"id":"7f020e23c7adfd0b","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-room-7f020e","errorCode":null,"errorMessage":"error-invalid-room","messagePattern":"error-invalid-room","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/ee/server/local-services/omnichannel.internalService.ts","lineNumber":41,"sourceCode":"\n\tlogger: Logger;\n\n\tconstructor() {\n\t\tsuper();\n\t\tthis.logger = new Logger('OmnichannelEE');\n\t}\n\n\tasync placeRoomOnHold(\n\t\troom: Pick<IOmnichannelRoom, '_id' | 't' | 'open' | 'onHold'>,\n\t\tcomment: string,\n\t\tonHoldBy: Pick<IUser, '_id' | 'username' | 'name'>,\n\t) {\n\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","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/ee/server/local-services/omnichannel.internalService.ts#L23-L59","documentation":"Thrown by placeRoomOnHold in omnichannel.internalService.ts:41 when the room is falsy or fails isOmnichannelRoom (room.t !== 'l'). This guards the hold operation so non-omnichannel or untyped rooms never enter the hold pipeline. NOTE: plain `new Error('error-invalid-room')`; followed immediately by related guards for closed and already-on-hold states.","triggerScenarios":"Calling placeRoomOnHold with a room whose type is not 'l' (e.g. a channel, direct message, or a hand-built object with the wrong/missing t field), or with null/undefined after a bad fetch.","commonSituations":"Service routing bug passes a non-omnichannel room; room fetched with a projection that excluded t; cross-feature integration assumes all rooms are placeable on hold.","solutions":["Fetch the room with the t field included and confirm isOmnichannelRoom(room) before calling.","Type-narrow the caller to omnichannel rooms (AtLeast<IOmnichannelRoom,...>) at the boundary.","Match by message 'error-invalid-room' and reject the request as 400."],"exampleFix":"// before\nconst room = await Rooms.findOneById(rid, { projection: { open: 1, onHold: 1 } });\nawait svc.placeRoomOnHold(room, comment, user);\n\n// after\nimport { isOmnichannelRoom } from '@rocket.chat/core-typings';\nconst room = await Rooms.findOneById(rid, { projection: { t: 1, open: 1, onHold: 1 } });\nif (!room || !isOmnichannelRoom(room)) throw new Error('not an omnichannel room');\nawait svc.placeRoomOnHold(room, comment, user);","handlingStrategy":"type-guard","validationCode":"import { isOmnichannelRoom } from '@rocket.chat/core-typings';\nconst room = await Rooms.findOneById(rid, { projection: { t: 1, open: 1, onHold: 1 } });\nif (!room || !isOmnichannelRoom(room)) throw new Error('not an omnichannel room');","typeGuard":"import { isOmnichannelRoom, type IOmnichannelRoom, type IRoom } from '@rocket.chat/core-typings';\n// isOmnichannelRoom = (room: Pick<IRoom,'t'>): room is IOmnichannelRoom & IRoom => room.t === 'l';","tryCatchPattern":"try { await svc.placeRoomOnHold(room, comment, user); }\ncatch (e) {\n  if (e instanceof Error && e.message === 'error-invalid-room') return res.status(400).send({ error: 'invalid-room' });\n  throw e;\n}","preventionTips":["Always project the t field when fetching rooms for hold operations.","Narrow with isOmnichannelRoom at the service boundary."],"tags":["omnichannel","room-state","validation","plain-error"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}