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

  1. Use the rid returned together with the token by the livechat session endpoints (e.g. livechat/room) so the pair is always consistent
  2. If the conversation was deleted, start a new session to obtain a fresh rid
  3. 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

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)