RocketChat/Rocket.Chat · error · Error

invalid-agent

Error message

invalid-agent

What it means

Thrown by GET /api/v1/livechat/agent.info/:rid/:token when the visitor token and room both resolve but no agent can be derived. The handler computes `room?.servedBy && (await findAgent(room.servedBy._id))`, which is falsy in two cases: the room has no `servedBy` yet (chat still queued/unassigned), or `findAgent` (normalizeAgent over the user id in `servedBy._id`) cannot resolve a live omnichannel agent.

Solutions

  1. If the chat was just created, treat the error as 'not assigned yet': wait for the agent-joined callback/room update (or poll livechat/room) and only call agent.info once servedBy is present.
  2. Fetch the room (e.g. livechat/room with the token) and confirm room.servedBy exists before requesting agent info.
  3. If servedBy exists but the error persists, verify the user behind servedBy._id still exists, is active, and is registered as a livechat agent; reassign the room if not.
  4. On the widget, degrade gracefully (hide the agent header) instead of surfacing the raw error.

Example fix

// before
const { agent } = await getAgentInfo({ rid, token }); // throws invalid-agent while queued

// after
const { room } = await getRoom(token);
if (!room?.servedBy) {
  // still queued; retry when the room update announces the agent
  await waitForAgentAssigned(rid);
}
const { agent } = await getAgentInfo({ rid, token });
Defensive patterns

Strategy: validation

Validate before calling

// Before agent.info, confirm the room actually has an agent assigned
const { room } = await getRoom(token); // livechat/room resolves by visitor token
if (!room?.servedBy?._id) {
  // still queued: subscribe to room updates / retry later instead of calling agent.info
  throw new PendingAssignmentError('agent not assigned yet');
}
const { agent } = await getAgentInfo(room._id, token);

Type guard

function isServedRoom(room: unknown): room is { servedBy: { _id: string } } {
  return typeof room === 'object' && room !== null &&
    typeof (room as any).servedBy?._id === 'string';
}

Try / catch

try { agent = await getAgentInfo(rid, token); } catch (e) { if (e.message === 'invalid-agent') { /* treat as pending assignment: retry with backoff until servedBy exists */ } else throw e; }

Prevention

When it happens

Trigger: Calling livechat/agent.info right after creating the room, before RoutingManager assigns an agent (servedBy missing); calling it after the serving agent was deleted, deactivated, or removed from omnichannel while the room still references their id.

Common situations: Widget polling agent info in the gap between livechat/room creation and agent assignment (race); agent removed mid-conversation; stale room.servedBy pointing at a user deleted from the Users collection; testing on a fresh workspace where rooms sit in queue.

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


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/6e058853d97ad052. Report an issue: GitHub.

Appendix: source

Thrown at apps/meteor/server/api/v1/omnichannel/agent.ts:38

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);
			if (room) {
				return API.v1.success();
			}

			let { department } = this.queryParams;

View on GitHub (pinned to b2c16d5842)