{"record":{"id":"0438361059391499","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-agent","errorCode":"error-invalid-agent","errorMessage":"error-invalid-agent","messagePattern":"error-invalid-agent","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/omnichannel/Helper.ts","lineNumber":497,"sourceCode":"\t\t\tmentionIds: [],\n\t\t});\n\t}\n};\n\nexport const forwardRoomToAgent = async (room: IOmnichannelRoom, transferData: TransferData) => {\n\tif (!room?.open) {\n\t\treturn false;\n\t}\n\n\tlogger.debug({\n\t\tmsg: 'Forwarding room to agent',\n\t\troomId: room._id,\n\t\tuserId: transferData.userId,\n\t});\n\n\tconst { userId: agentId, clientAction } = transferData;\n\tif (!agentId) {\n\t\tthrow new Error('error-invalid-agent');\n\t}\n\tconst user = await Users.findOneOnlineAgentById(\n\t\tagentId,\n\t\tsettings.get<boolean>('Livechat_enabled_when_agent_idle'),\n\t\tsettings.get<boolean>('Livechat_accept_chats_with_no_agents'),\n\t\t{},\n\t);\n\tif (!user) {\n\t\tlogger.debug({\n\t\t\tmsg: 'Agent is offline. Cannot forward',\n\t\t\tagentId,\n\t\t});\n\t\tthrow new Error('error-user-is-offline');\n\t}\n\n\tconst { _id: rid, servedBy: oldServedBy } = room;\n\tconst inquiry = await LivechatInquiry.findOneByRoomId(rid, {});\n\tif (!inquiry) {","sourceCodeStart":479,"sourceCodeEnd":515,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/omnichannel/Helper.ts#L479-L515","documentation":"forwardRoomToAgent requires transferData.userId (the target agent); a falsy value throws error-invalid-agent before any database lookup. This is a required-argument guard, not a lookup failure - an existing-but-offline agent produces error-user-is-offline instead.","triggerScenarios":"Calling the livechat transfer/forward flow (e.g. livechat/room.forward REST endpoint) without userId in the payload; building transferData from a form where the agent select was cleared; field-name mismatch such as user_id vs userId.","commonSituations":"Frontend sends only { roomId, departmentId } but routes into the agent-forward branch; integrations that assume the agent is optional; agent picker cleared before submit.","solutions":["Include a non-empty userId in transferData when forwarding to a specific agent","If forwarding to a department only, use the department path that does not require userId","Validate the payload shape at the API boundary before invoking the transfer"],"exampleFix":"// before\nawait forwardRoomToAgent(room, { clientAction: 'forward' }, guest);\n\n// after\nconst { userId } = transferData;\nif (!userId) throw new Error('error-invalid-agent');\nawait forwardRoomToAgent(room, { ...transferData, userId }, guest);","handlingStrategy":"validation","validationCode":"const isNonEmptyId = (v: unknown): v is string => typeof v === 'string' && v.trim().length > 0;\nif (!isNonEmptyId(transferData.userId)) {\n  throw new Error('error-invalid-agent');\n}\nawait forwardRoomToAgent(room, transferData, guest);","typeGuard":"const isNonEmptyId = (v: unknown): v is string => typeof v === 'string' && v.trim().length > 0;","tryCatchPattern":"try {\n  await forwardRoomToAgent(room, transferData, guest);\n} catch (err) {\n  if (err instanceof Error && err.message === 'error-invalid-agent') {\n    // payload lost userId - rebuild transferData and resubmit\n  } else throw err;\n}","preventionTips":["Validate transfer payload shape (userId/departmentId presence) at the API boundary","Keep the agent picker required when the forward target is an agent"],"tags":["omnichannel","livechat","transfer","missing-parameter","rocket-chat"],"backgroundTag":"missing-required-parameter","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}