{"record":{"id":"43a0f9c777df6a7b","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-visitor","errorCode":null,"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/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/api/v1/omnichannel/room.ts#L291-L327","documentation":"Thrown by POST /api/v1/livechat/room.forward when LivechatVisitors.findOneEnabledById(room.v?._id) returns null — the room's visitor either does not exist or is disabled. findOneEnabledById filters on the visitor's enabled flag, so a deliberately disabled visitor fails even though the room references it.","triggerScenarios":"Forwarding a room whose visitor record was deleted, or whose visitor was disabled (blocked) after the conversation opened — e.g. an agent blocks the visitor mid-chat and a supervisor then attempts a forward.","commonSituations":"Visitor blocking/cleanup automations racing with transfers; test rooms created against visitors later wiped from the database; data imports that dropped visitors but kept rooms.","solutions":["Check the visitor: GET livechat/visitor.info (agent API) by token/_id; if disabled, re-enable the visitor or abandon the forward","If the visitor was blocked in error, unblock and retry","For orphaned rooms (visitor gone), close the room instead of forwarding it"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await forward(roomId, payload); } catch (e) { if (e.message === 'error-invalid-visitor') { await closeOrphanedRoom(roomId); return; } throw e; }","preventionTips":["Check visitor status before building forward UI when blocking is common in the workflow","Do not retry a forward whose visitor is disabled — the room is effectively dead","Prefer closing orphaned rooms over retrying transfers"],"tags":["omnichannel","livechat","visitor-disabled","transfer","rest-api"],"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"}