{"record":{"id":"d84621c1af772c75","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-room-d84621","errorCode":"error-invalid-room","errorMessage":"error-invalid-room","messagePattern":"error-invalid-room","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/omnichannel/rooms.ts","lineNumber":275,"sourceCode":"\ttry {\n\t\tawait saveTransferHistory(room, transferData);\n\t\tawait RoutingManager.unassignAgent(inquiry, departmentId);\n\t} catch (err) {\n\t\tlivechatLogger.error({ err });\n\t\tthrow new Meteor.Error('error-returning-inquiry');\n\t}\n\n\tcallbacks.runAsync('livechat:afterReturnRoomAsInquiry', { room });\n\n\treturn true;\n}\n\nexport async function removeOmnichannelRoom(rid: string) {\n\tlivechatLogger.debug({ msg: 'Deleting room', roomId: rid });\n\tcheck(rid, String);\n\tconst room = await LivechatRooms.findOneById(rid);\n\tif (!room) {\n\t\tthrow new Meteor.Error('error-invalid-room', 'Invalid room');\n\t}\n\n\tif (!isOmnichannelRoom(room)) {\n\t\tthrow new Meteor.Error('error-this-is-not-a-livechat-room');\n\t}\n\n\tif (room.open) {\n\t\tthrow new Meteor.Error('error-room-is-not-closed');\n\t}\n\n\tconst inquiry = await LivechatInquiry.findOneByRoomId(rid);\n\n\tconst result = await Promise.allSettled([\n\t\tMessages.removeByRoomId(rid),\n\t\tReadReceipts.removeByRoomId(rid),\n\t\tSubscriptions.removeByRoomId(rid, {\n\t\t\tasync onTrash(doc) {\n\t\t\t\tvoid notifyOnSubscriptionChanged(doc, 'removed');","sourceCodeStart":257,"sourceCodeEnd":293,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/omnichannel/rooms.ts#L257-L293","documentation":"removeOmnichannelRoom(rid) loads the room via LivechatRooms.findOneById; a null lookup throws Meteor.Error('error-invalid-room', 'Invalid room') as the first guard, before any type or state validation.","triggerScenarios":"Calling removeOmnichannelRoom with a rid that does not exist — typo, room already removed, or a client acting on a stale cached id.","commonSituations":"Deletion retried after success; rid copied from the wrong field; room already purged by a retention job.","solutions":["Verify the room exists (e.g., GET /api/v1/rooms.info) before deleting.","Treat 'error-invalid-room' on retry as success for idempotent deletion workflows.","Re-fetch the rid at deletion time instead of caching it long-term."],"exampleFix":"// before\nawait removeOmnichannelRoom(rid); // throws error-invalid-room\n\n// after\nif (!(await LivechatRooms.findOneById(rid, { projection: { _id: 1 } }))) {\n\t// already gone - treat as success for idempotent deletion\n\treturn;\n}\nawait removeOmnichannelRoom(rid);","handlingStrategy":"validation","validationCode":"if (!(await LivechatRooms.findOneById(rid, { projection: { _id: 1 } }))) {\n\t// room already gone: treat as success for idempotent deletion\n\treturn;\n}\nawait removeOmnichannelRoom(rid);","typeGuard":"const isExistingRoom = async (rid: string): Promise<boolean> =>\n\tBoolean(await LivechatRooms.findOneById(rid, { projection: { _id: 1 } }));","tryCatchPattern":"try {\n\tawait removeOmnichannelRoom(rid);\n} catch (err) {\n\tif (err instanceof Meteor.Error && err.error === 'error-invalid-room') {\n\t\t// already removed or wrong rid: refresh the source of rids\n\t\treturn;\n\t}\n\tthrow err;\n}","preventionTips":["Confirm the room exists (rooms.info) before issuing a delete.","Make deletion workflows idempotent: 'not found' equals success.","Do not cache rids for later deletion; re-resolve them at operation time."],"tags":["omnichannel","livechat","rooms","deletion"],"backgroundTag":"resource-not-found","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}