RocketChat/Rocket.Chat · error · Error
invalid-room
Error message
invalid-room
What it means
Second guard on GET /api/v1/livechat/agent.info/:rid/:token: findRoom(token, rid) resolves to LivechatRooms.findOneByIdAndVisitorToken(rid, token) (apps/meteor/server/api/v1/omnichannel/lib/livechat.ts:59-74). It returns null - and the route throws Error('invalid-room') at agent.ts:33 - when no omnichannel room exists with that _id, or the room exists but does not belong to the visitor holding that token.
Solutions
- Use the rid returned together with the token by the livechat session endpoints (e.g. livechat/room) so the pair is always consistent
- If the conversation was deleted, start a new session to obtain a fresh rid
- Verify the room exists with an agent-side lookup (rooms.info with an authenticated agent token) when debugging
Defensive patterns
Strategy: validation
Validate before calling
const { room } = await fetch(`${server}/api/v1/livechat/room?token=${encodeURIComponent(token)}`).then((r) => r.json());
if (!room?._id || room._id !== rid) {
throw new Error(`rid ${rid} does not belong to this visitor token - use the rid from the current session`);
}
await fetch(`${server}/api/v1/livechat/agent.info/${rid}/${token}`); Type guard
const isRoomOfVisitor = (room: { _id?: string; v?: { token?: string } } | null, rid: string, token: string): room is { _id: string; v: { token: string } } =>
!!room && room._id === rid && room.v?.token === token; Try / catch
try {
const res = await fetch(`${server}/api/v1/livechat/agent.info/${rid}/${token}`);
if (!res.ok) throw await res.json();
} catch (err) {
if (err?.error === 'invalid-room') {
// token was valid (invalid-token would have fired first): the rid is wrong or stale
session = await startNewLivechatSession(); // fetch a fresh rid paired with the token
}
} Prevention
- Always take rid from the same response that gave you the token (livechat/room), never from cache
- When a conversation can be deleted server-side, design the client to recover by starting a new session on invalid-room
When it happens
Trigger: rid typo'd or truncated; rid belonging to another visitor's conversation; room deleted after the conversation ended; token from one session combined with rid from another.
Common situations: Widgets caching a stale rid after the conversation was closed/removed; hand-built URLs mixing values from different sessions; environment migrations that copied rooms but not the visitor-token linkage.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/d2c523610c34d9ac.
Report an issue: GitHub.
Appendix: source
Thrown at apps/meteor/server/api/v1/omnichannel/agent.ts:33
import { findRoom, findGuest, findAgent, findOpenRoom } from './lib/livechat';
import { hasPermissionAsync } from '../../../lib/authorization/hasPermission';
import { hasRoleAsync } from '../../../lib/authorization/hasRole';
import { RoutingManager } from '../../../lib/omnichannel/RoutingManager';
import { getRequiredDepartment } from '../../../lib/omnichannel/departmentsLib';
import { saveAgentInfo } from '../../../lib/omnichannel/omni-users';
import { setUserStatusLivechat, allowAgentChangeServiceStatus } from '../../../lib/omnichannel/utils';
import type { ExtractRoutesFromAPI } from '../../ApiClass';
API.v1.addRoute('livechat/agent.info/:rid/:token', {
async get() {
const visitor = await findGuest(this.urlParams.token);
if (!visitor) {
throw new Error('invalid-token');
}
const room = await findRoom(this.urlParams.token, this.urlParams.rid);
if (!room) {
throw new Error('invalid-room');
}
const agent = room?.servedBy && (await findAgent(room.servedBy._id));
if (!agent) {
throw new Error('invalid-agent');
}
return API.v1.success({ agent });
},
});
API.v1.addRoute(
'livechat/agent.next/:token',
{ validateParams: isGETAgentNextToken },
{
async get() {
const { token } = this.urlParams;
const room = await findOpenRoom(token, undefined, this.userId);View on GitHub (pinned to b2c16d5842)