RocketChat/Rocket.Chat · error · Error
invalid-room
Error message
invalid-room
What it means
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.
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).
Example fix
// before (resume with a stale rid that may be closed)
const res = await fetch(`/api/v1/livechat/room?token=${token}&rid=${cachedRid}`);
// after (drop rid on failure and let the server return/create the open room)
let res = await fetch(`/api/v1/livechat/room?token=${token}&rid=${cachedRid}`);
let body = await res.json();
if (!body.success && body.error === 'invalid-room') {
res = await fetch(`/api/v1/livechat/room?token=${token}`);
body = await res.json();
}
const room = body.room; // may be a new room (body.newRoom === true) Defensive patterns
Strategy: fallback
Validate before calling
// Prefer the rid-less call, which returns or creates the open room
const res = await fetch(`/api/v1/livechat/room?token=${encodeURIComponent(token)}`);
const body = await res.json();
if (!body.success) throw new Error(`room resolve failed: ${body.error}`);
const rid: string = body.room._id; // fresh room when body.newRoom === true Try / catch
if (isLivechatErrorResponse(body) && body.error === 'invalid-room') {
// rid closed or mismatched: clear cache and fall back to rid-less resolve (creates a new room)
cachedRid = undefined;
const fresh = await (await fetch(`/api/v1/livechat/room?token=${encodeURIComponent(token)}`)).json();
handleRoom(fresh.room, fresh.newRoom);
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- error-invalid-room
- agent-not-found
- error-forwarding-chat
- error-invalid-department-unit
- error-invalid-user
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/aa20d4d7643d3be2.
Report an issue: GitHub.
Appendix: source
Thrown at apps/meteor/server/api/v1/omnichannel/room.ts:120
},
};
const newRoom = await createRoom({
visitor: guest,
roomInfo,
agent,
extraData: extraParams as IOmnichannelInquiryExtraData,
});
return API.v1.success({
room: newRoom,
newRoom: true,
});
}
const froom = await LivechatRooms.findOneOpenByRoomIdAndVisitorToken(rid, token, {});
if (!froom) {
throw new Error('invalid-room');
}
return API.v1.success({ room: froom, newRoom: false });
},
},
);
// Note: use this route if a visitor is closing a room
// If a RC user(like eg agent) is closing a room, use the `livechat/room.closeByUser` route
API.v1.addRoute(
'livechat/room.close',
{ validateParams: isPOSTLivechatRoomCloseParams },
{
async post() {
const { rid, token } = this.bodyParams;
if (!rcSettings.get('Omnichannel_allow_visitors_to_close_conversation')) {
throw new Error('error-not-allowed-to-close-conversation');View on GitHub (pinned to b2c16d5842)