{"record":{"id":"9b3ee251ec18bd35","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-room-9b3ee2","errorCode":null,"errorMessage":"error-invalid-room","messagePattern":"error-invalid-room","errorType":"exception","errorClass":"Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/server/api/v1/omnichannel/room.ts","lineNumber":296,"sourceCode":"\n\t\t\treturn API.v1.success({ rid, data: updateData });\n\t\t},\n\t},\n);\n\nAPI.v1.addRoute(\n\t'livechat/room.forward',\n\t{ authRequired: true, permissionsRequired: ['view-l-room', 'transfer-livechat-guest'], validateParams: isLiveChatRoomForwardProps },\n\t{\n\t\tasync post() {\n\t\t\tconst transferData = this.bodyParams as typeof this.bodyParams & {\n\t\t\t\ttransferredBy: TransferByData;\n\t\t\t\ttransferredTo?: { _id: string; username?: string; name?: string };\n\t\t\t};\n\n\t\t\tconst room = await LivechatRooms.findOneById(this.bodyParams.roomId);\n\t\t\tif (room?.t !== 'l') {\n\t\t\t\tthrow new Error('error-invalid-room');\n\t\t\t}\n\n\t\t\tif (!room.open) {\n\t\t\t\tthrow new Error('This_conversation_is_already_closed');\n\t\t\t}\n\n\t\t\tif (!(await Omnichannel.isWithinMACLimit(room))) {\n\t\t\t\tthrow new Error('error-mac-limit-reached');\n\t\t\t}\n\n\t\t\tconst guest = await LivechatVisitors.findOneEnabledById(room.v?._id);\n\t\t\tif (!guest) {\n\t\t\t\tthrow new Error('error-invalid-visitor');\n\t\t\t}\n\n\t\t\ttransferData.transferredBy = normalizeTransferredByData(this.user, room);\n\t\t\tif (transferData.userId) {\n\t\t\t\tconst userToTransfer = await Users.findOneById(transferData.userId);","sourceCodeStart":278,"sourceCodeEnd":314,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/api/v1/omnichannel/room.ts#L278-L314","documentation":"Thrown by POST /api/v1/livechat/room.forward when LivechatRooms.findOneById(roomId) returns null OR the found room's type is not 'l'. Both 'no such room' and 'room exists but is not an omnichannel room' collapse into this single error before any transfer logic runs.","triggerScenarios":"Authenticated call (requires view-l-room + transfer-livechat-guest permissions) with a roomId that does not exist, a roomId of a regular channel/group/DM, or a truncated/malformed ObjectId.","commonSituations":"Integration forwards a rid taken from a normal chat payload instead of an omnichannel room; room deleted between listing and forwarding; copy/paste truncation of the _id.","solutions":["Confirm the rid refers to an omnichannel room (e.g. GET livechat/rooms, or check room.t === 'l' in whatever source produced it)","Re-fetch the room list and use a current _id","Check server logs for the exact roomId received to spot truncation/encoding issues"],"exampleFix":"// before\nforward({ roomId: channel._id, ... }); // channel rid, t !== 'l'\n\n// after\nif (room.t !== 'l') throw new Error('cannot forward a non-omnichannel room');\nforward({ roomId: room._id, ... });","handlingStrategy":"type-guard","validationCode":"const rooms = await (await fetch(`${url}/api/v1/livechat/rooms`, { headers: authHeaders })).json();\nconst target = rooms.rooms?.find((r) => r._id === roomId && r.t === 'l');\nif (!target) throw new Error('cannot forward: not an omnichannel room');","typeGuard":"function isLivechatRoomId(room) {\n  return Boolean(room) && room.t === 'l' && typeof room._id === 'string';\n}","tryCatchPattern":"try { await forward(roomId, payload); } catch (e) { if (e.message === 'error-invalid-room') { await refreshRoomCache(); return; } throw e; }","preventionTips":["Source rids only from omnichannel room listings (t === 'l')","Re-validate cached rids before forwarding","Never forward ids taken from channel/DM payloads"],"tags":["omnichannel","livechat","room-not-found","transfer","rest-api"],"backgroundTag":"resource-not-found","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"}