{"record":{"id":"43a0f9c777df6a7b","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-visitor","errorCode":"error-invalid-visitor","errorMessage":"error-invalid-visitor","messagePattern":"error-invalid-visitor","errorType":"exception","errorClass":"Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/server/api/v1/omnichannel/room.ts","lineNumber":309,"sourceCode":"\t\t\t\ttransferredTo?: { _id: string; username?: string; name?: string };\n\t\t\t};\n\n\t\t\tconst room = await LivechatRooms.findOneById(this.bodyParams.roomId);\n\t\t\tif (room?.t !== 'l') {\n\t\t\t\tthrow new Error('error-invalid-room');\n\t\t\t}\n\n\t\t\tif (!room.open) {\n\t\t\t\tthrow new Error('This_conversation_is_already_closed');\n\t\t\t}\n\n\t\t\tif (!(await Omnichannel.isWithinMACLimit(room))) {\n\t\t\t\tthrow new Error('error-mac-limit-reached');\n\t\t\t}\n\n\t\t\tconst guest = await LivechatVisitors.findOneEnabledById(room.v?._id);\n\t\t\tif (!guest) {\n\t\t\t\tthrow new Error('error-invalid-visitor');\n\t\t\t}\n\n\t\t\ttransferData.transferredBy = normalizeTransferredByData(this.user, room);\n\t\t\tif (transferData.userId) {\n\t\t\t\tconst userToTransfer = await Users.findOneById(transferData.userId);\n\t\t\t\tif (userToTransfer) {\n\t\t\t\t\ttransferData.transferredTo = {\n\t\t\t\t\t\t_id: userToTransfer._id,\n\t\t\t\t\t\tusername: userToTransfer.username,\n\t\t\t\t\t\tname: userToTransfer.name,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst chatForwardedResult = await transfer(room, guest, transferData);\n\t\t\tif (!chatForwardedResult) {\n\t\t\t\tthrow new Error('error-forwarding-chat');\n\t\t\t}","sourceCodeStart":291,"sourceCodeEnd":327,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/server/api/v1/omnichannel/room.ts#L291-L327","documentation":"Thrown by POST livechat/room.forward when LivechatVisitors.findOneEnabledById(room.v._id) returns null. The visitor referenced by the room either does not exist or is disabled (archived/deleted via visitor management). Without a live guest record the transfer cannot proceed because guest identity is required for routing and audit.","triggerScenarios":"POST livechat/room.forward on a room whose v._id points to a visitor that was deleted, archived, or had its enabled flag cleared by a compliance/privacy job. Also occurs if the room document is orphaned (visitor removed manually from the DB).","commonSituations":"GDPR/cleanup scripts that delete visitors but leave rooms open; LDAP/SSO user purges that cascade to visitors; manual DB surgery that broke the room.visitor link; a migration that recreated visitors with new _ids.","solutions":["Verify the visitor exists and is enabled via GET livechat/visitors.info?token=... or by querying LivechatVisitors directly.","If the visitor was archived, restore it before retrying the forward.","Close the orphaned room (livechat/room.close) and start a new conversation rather than transferring a broken one.","Audit compliance/cleanup jobs that delete visitors to also close their open rooms."],"exampleFix":"// before\nawait POST('/api/v1/livechat/room.forward', { roomId, userId });\n\n// after\nconst visitor = await LivechatVisitors.findOneEnabledById(room.v?._id);\nif (!visitor) {\n  await POST('/api/v1/livechat/room.close', { roomId, comment: 'visitor missing' });\n  return;\n}\nawait POST('/api/v1/livechat/room.forward', { roomId, userId });","handlingStrategy":"validation","validationCode":"const visitor = await LivechatVisitors.findOneEnabledById(room.v?._id);\nif (!visitor) throw new ClientError('visitor-disabled-or-missing');","typeGuard":"const hasEnabledVisitor = (room: { v?: { _id?: string } } | null): room is { v: { _id: string } } =>\n  !!room?.v?._id;","tryCatchPattern":"try {\n  await POST('/api/v1/livechat/room.forward', { roomId, userId });\n} catch (e) {\n  if (e.message === 'error-invalid-visitor') { closeOrphanedRoom(roomId); return; }\n  throw e;\n}","preventionTips":["Ensure compliance/cleanup jobs that delete visitors also close their open rooms.","Run periodic integrity checks for rooms whose v._id no longer resolves to an enabled visitor.","Never delete visitors while their rooms are still open."],"tags":["omnichannel","livechat","visitor","data-integrity","transfer"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}