{"record":{"id":"6e058853d97ad052","repo":"RocketChat/Rocket.Chat","slug":"invalid-agent","errorCode":null,"errorMessage":"invalid-agent","messagePattern":"invalid-agent","errorType":"exception","errorClass":"Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/server/api/v1/omnichannel/agent.ts","lineNumber":38,"sourceCode":"import { saveAgentInfo } from '../../../lib/omnichannel/omni-users';\nimport { setUserStatusLivechat, allowAgentChangeServiceStatus } from '../../../lib/omnichannel/utils';\nimport type { ExtractRoutesFromAPI } from '../../ApiClass';\n\nAPI.v1.addRoute('livechat/agent.info/:rid/:token', {\n\tasync get() {\n\t\tconst visitor = await findGuest(this.urlParams.token);\n\t\tif (!visitor) {\n\t\t\tthrow new Error('invalid-token');\n\t\t}\n\n\t\tconst room = await findRoom(this.urlParams.token, this.urlParams.rid);\n\t\tif (!room) {\n\t\t\tthrow new Error('invalid-room');\n\t\t}\n\n\t\tconst agent = room?.servedBy && (await findAgent(room.servedBy._id));\n\t\tif (!agent) {\n\t\t\tthrow new Error('invalid-agent');\n\t\t}\n\n\t\treturn API.v1.success({ agent });\n\t},\n});\n\nAPI.v1.addRoute(\n\t'livechat/agent.next/:token',\n\t{ validateParams: isGETAgentNextToken },\n\t{\n\t\tasync get() {\n\t\t\tconst { token } = this.urlParams;\n\t\t\tconst room = await findOpenRoom(token, undefined, this.userId);\n\t\t\tif (room) {\n\t\t\t\treturn API.v1.success();\n\t\t\t}\n\n\t\t\tlet { department } = this.queryParams;","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/api/v1/omnichannel/agent.ts#L20-L56","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","Fetch the room (e.g. livechat/room with the token) and confirm room.servedBy exists before requesting agent info.","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.","On the widget, degrade gracefully (hide the agent header) instead of surfacing the raw error."],"exampleFix":"// before\nconst { agent } = await getAgentInfo({ rid, token }); // throws invalid-agent while queued\n\n// after\nconst { room } = await getRoom(token);\nif (!room?.servedBy) {\n  // still queued; retry when the room update announces the agent\n  await waitForAgentAssigned(rid);\n}\nconst { agent } = await getAgentInfo({ rid, token });","handlingStrategy":"validation","validationCode":"// Before agent.info, confirm the room actually has an agent assigned\nconst { room } = await getRoom(token); // livechat/room resolves by visitor token\nif (!room?.servedBy?._id) {\n  // still queued: subscribe to room updates / retry later instead of calling agent.info\n  throw new PendingAssignmentError('agent not assigned yet');\n}\nconst { agent } = await getAgentInfo(room._id, token);","typeGuard":"function isServedRoom(room: unknown): room is { servedBy: { _id: string } } {\n  return typeof room === 'object' && room !== null &&\n    typeof (room as any).servedBy?._id === 'string';\n}","tryCatchPattern":"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; }","preventionTips":["Only call agent.info after the room announces an agent (agent-added callback).","Treat invalid-agent as 'queued', not as a hard failure.","Alert admins when servedBy points at a missing user so rooms get reassigned."],"tags":["omnichannel","livechat","agent","rest-api","visitor-widget"],"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"}