{"record":{"id":"aa20d4d7643d3be2","repo":"RocketChat/Rocket.Chat","slug":"invalid-room-aa20d4","errorCode":null,"errorMessage":"invalid-room","messagePattern":"invalid-room","errorType":"exception","errorClass":"Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/server/api/v1/omnichannel/room.ts","lineNumber":120,"sourceCode":"\t\t\t\t\t},\n\t\t\t\t};\n\n\t\t\t\tconst newRoom = await createRoom({\n\t\t\t\t\tvisitor: guest,\n\t\t\t\t\troomInfo,\n\t\t\t\t\tagent,\n\t\t\t\t\textraData: extraParams as IOmnichannelInquiryExtraData,\n\t\t\t\t});\n\n\t\t\t\treturn API.v1.success({\n\t\t\t\t\troom: newRoom,\n\t\t\t\t\tnewRoom: true,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst froom = await LivechatRooms.findOneOpenByRoomIdAndVisitorToken(rid, token, {});\n\t\t\tif (!froom) {\n\t\t\t\tthrow new Error('invalid-room');\n\t\t\t}\n\n\t\t\treturn API.v1.success({ room: froom, newRoom: false });\n\t\t},\n\t},\n);\n\n// Note: use this route if a visitor is closing a room\n// If a RC user(like eg agent) is closing a room, use the `livechat/room.closeByUser` route\nAPI.v1.addRoute(\n\t'livechat/room.close',\n\t{ validateParams: isPOSTLivechatRoomCloseParams },\n\t{\n\t\tasync post() {\n\t\t\tconst { rid, token } = this.bodyParams;\n\n\t\t\tif (!rcSettings.get('Omnichannel_allow_visitors_to_close_conversation')) {\n\t\t\t\tthrow new Error('error-not-allowed-to-close-conversation');","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/api/v1/omnichannel/room.ts#L102-L138","documentation":"Thrown by GET /api/v1/livechat/room?token=...&rid=... when LivechatRooms.findOneOpenByRoomIdAndVisitorToken(rid, token) returns null. Unlike the message endpoints' findRoom, this lookup only matches OPEN rooms — so both a wrong/mismatched rid AND an already-closed room produce 'invalid-room' here.","triggerScenarios":"GET livechat/room with rid of a room that was closed (visitor closed it, agent closed it, or closeRoom/closeLivechatRoom ran) or rid belonging to a different visitor token. Calling with rid after the conversation ended is the classic case.","commonSituations":"Widget resumes a session after the chat was closed server-side (SLA timeout, agent closing, visitor close earlier) and re-requests the room by id; integrations storing room ids across restarts without handling closure; race where the room closes between listing and fetching.","solutions":["Omit rid and call GET livechat/room?token=... alone — it returns the currently open room or creates a new one, handling the closed-room case gracefully.","On 'invalid-room' here, treat the conversation as ended: clear cached rid and start a fresh room.","Confirm rid actually belongs to this token (ownership mismatch gives the same error)."],"exampleFix":"// before (resume with a stale rid that may be closed)\nconst res = await fetch(`/api/v1/livechat/room?token=${token}&rid=${cachedRid}`);\n\n// after (drop rid on failure and let the server return/create the open room)\nlet res = await fetch(`/api/v1/livechat/room?token=${token}&rid=${cachedRid}`);\nlet body = await res.json();\nif (!body.success && body.error === 'invalid-room') {\n  res = await fetch(`/api/v1/livechat/room?token=${token}`);\n  body = await res.json();\n}\nconst room = body.room; // may be a new room (body.newRoom === true)","handlingStrategy":"fallback","validationCode":"// Prefer the rid-less call, which returns or creates the open room\nconst res = await fetch(`/api/v1/livechat/room?token=${encodeURIComponent(token)}`);\nconst body = await res.json();\nif (!body.success) throw new Error(`room resolve failed: ${body.error}`);\nconst rid: string = body.room._id; // fresh room when body.newRoom === true","typeGuard":null,"tryCatchPattern":"if (isLivechatErrorResponse(body) && body.error === 'invalid-room') {\n  // rid closed or mismatched: clear cache and fall back to rid-less resolve (creates a new room)\n  cachedRid = undefined;\n  const fresh = await (await fetch(`/api/v1/livechat/room?token=${encodeURIComponent(token)}`)).json();\n  handleRoom(fresh.room, fresh.newRoom);\n}","preventionTips":["Remember this route only matches OPEN rooms — closure and wrong-id are indistinguishable here.","Clear the cached rid whenever the conversation-finished event arrives.","Prefer the rid-less form for session resume; use rid only when you specifically need that room."],"tags":["livechat","omnichannel","room-not-found","room-closed","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"}