{"record":{"id":"2bfb4e8b009b0dce","repo":"RocketChat/Rocket.Chat","slug":"invalid-target-agent-cannot-transfer","errorCode":null,"errorMessage":"Invalid target agent, cannot transfer","messagePattern":"Invalid target agent, cannot transfer","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/app/apps/server/bridges/livechat.ts","lineNumber":288,"sourceCode":"\t\t}\n\t\tconst { _id, username, name, type } = appUser;\n\t\tconst transferredBy = {\n\t\t\t_id,\n\t\t\tusername,\n\t\t\tname,\n\t\t\ttype,\n\t\t\tuserType: 'user',\n\t\t} as const;\n\n\t\tlet userId;\n\t\tlet transferredTo;\n\n\t\tif (targetAgent?.id) {\n\t\t\ttransferredTo = await Users.findOneAgentById(targetAgent.id, {\n\t\t\t\tprojection: { _id: 1, username: 1, name: 1 },\n\t\t\t});\n\t\t\tif (!transferredTo) {\n\t\t\t\tthrow new Error('Invalid target agent, cannot transfer');\n\t\t\t}\n\n\t\t\tuserId = transferredTo._id;\n\t\t}\n\n\t\t// #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed.\n\t\treturn transfer(\n\t\t\t(await this.orch.getConverters()?.get('rooms').convertAppRoom(currentRoom)) as IOmnichannelRoom,\n\t\t\tthis.orch.getConverters()?.get('visitors').convertAppVisitor(visitor),\n\t\t\t{ userId, departmentId, transferredBy, transferredTo },\n\t\t);\n\t}\n\n\tprotected async findVisitors(query: object, appId: string): Promise<Array<IVisitor>> {\n\t\tthis.orch.debugLog(`The App ${appId} is looking for livechat visitors.`);\n\n\t\tif (this.orch.isDebugging()) {\n\t\t\tconsole.warn('The method AppLivechatBridge.findVisitors is deprecated. Please consider using its alternatives');","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/app/apps/server/bridges/livechat.ts#L270-L306","documentation":"Thrown by AppLivechatBridge.transferVisitor when transferData.targetAgent.id is set but Users.findOneAgentById returns null. The bridge must resolve the target agent (id, username, name) to populate transferredTo, so an unknown or non-agent target id aborts the transfer.","triggerScenarios":"An App calls transfer with a targetAgent whose id does not resolve to an agent: the id is stale/deleted, belongs to a non-agent user, or was mistyped. findOneAgentById enforces both existence and agent status.","commonSituations":"App caches a target agent id that was later removed/demoted; App passes a username or token where an agent user id is expected; agent was removed from the livechat agents collection but not the Users collection; transfer initiated from stale UI state.","solutions":["Validate the target agent id with the livechat read accessor (findOneAgentById) before calling transfer.","Refresh cached agent ids at transfer time.","If the target is optional, fall back to department-based or automatic routing when the agent is missing.","Confirm the user is an active livechat agent, not just a regular user."],"exampleFix":"// before\nawait app.getLivechatModifier().transferVisitor(visitor, {\n  currentRoom: room,\n  targetAgent: { id: cachedAgentId },\n});\n\n// after\nconst targetAgent = cachedAgentId\n  ? await Users.findOneAgentById(cachedAgentId)\n  : undefined;\nif (cachedAgentId && !targetAgent) {\n  throw new Error(`Target agent ${cachedAgentId} is not available`);\n}\nawait app.getLivechatModifier().transferVisitor(visitor, {\n  currentRoom: room,\n  targetAgent: targetAgent ? { id: targetAgent._id } : undefined,\n});","handlingStrategy":"validation","validationCode":"if (transferData.targetAgent?.id) {\n  const target = await Users.findOneAgentById(transferData.targetAgent.id);\n  if (!target) {\n    throw new Error(`Target agent ${transferData.targetAgent.id} does not exist or is not an agent`);\n  }\n}\nawait app.getLivechatModifier().transferVisitor(visitor, transferData);","typeGuard":"const isResolvableTargetAgent = async (id: string): Promise<boolean> =>\n  Boolean(await Users.findOneAgentById(id, { projection: { _id: 1 } }));","tryCatchPattern":"try {\n  return await app.getLivechatModifier().transferVisitor(visitor, transferData);\n} catch (e) {\n  if ((e as Error).message === 'Invalid target agent, cannot transfer') {\n    // fall back to department-based or automatic routing\n  }\n  throw e;\n}","preventionTips":["Validate the target agent id with findOneAgentById before transferring.","Refresh cached agent ids at transfer time.","Fall back to department/auto routing when the agent is unavailable."],"tags":["apps-engine","livechat","transfer","agent","validation"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}