{"record":{"id":"69e0d6b64b5ef227","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-visitor-69e0d6","errorCode":"error-invalid-visitor","errorMessage":"error-invalid-visitor","messagePattern":"error-invalid-visitor","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/omnichannel/guests.ts","lineNumber":34,"sourceCode":"\nimport { parseAgentCustomFields } from './Helper';\nimport type { ICRMData } from './localTypes';\nimport { livechatLogger } from './logger';\nimport { trim } from '../../../lib/utils/stringUtils';\nimport { hasPermissionAsync } from '../authorization/hasPermission';\nimport { i18n } from '../i18n';\nimport { FileUpload } from '../media/file-upload';\nimport { notifyOnSubscriptionChanged, notifyOnLivechatInquiryChanged, notifyOnLivechatInquiryChangedByToken } from '../notifyListener';\n\nexport async function saveGuest(\n\tguestData: Pick<ILivechatVisitor, '_id' | 'name' | 'livechatData'> & { email?: string; phone?: string },\n\tuserId: string,\n) {\n\tconst { _id, name, email, phone, livechatData = {} } = guestData;\n\n\tconst visitor = await LivechatVisitors.findOneById(_id, { projection: { _id: 1 } });\n\tif (!visitor) {\n\t\tthrow new Error('error-invalid-visitor');\n\t}\n\n\tlivechatLogger.debug({ msg: 'Saving guest', guestData });\n\tconst updateData = {\n\t\t...(name && { name }),\n\t\t...(email && { email }),\n\t\t...(phone && { phone }),\n\t\tlivechatData: {},\n\t};\n\n\tconst customFields: Record<string, any> = {};\n\n\tif ((!userId || (await hasPermissionAsync(userId, 'edit-livechat-room-customfields'))) && Object.keys(livechatData).length) {\n\t\tlivechatLogger.debug({ msg: 'Saving custom fields for visitor', visitorId: _id, livechatData });\n\t\tfor await (const field of LivechatCustomField.findByScope('visitor')) {\n\t\t\tif (!livechatData.hasOwnProperty(field._id)) {\n\t\t\t\tcontinue;\n\t\t\t}","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/2a7de457074cbb4d4373fbd9a4e5bea292c9c764/apps/meteor/server/lib/omnichannel/guests.ts#L16-L52","documentation":"Thrown by `saveGuest(guestData, userId)` when `LivechatVisitors.findOneById(_id, { projection: { _id: 1 } })` returns null. The visitor must already exist — saveGuest only patches name/email/phone/livechatData on a known visitor record; it never creates one.","triggerScenarios":"Updating visitor attributes via the livechat guest-saving flow with a visitor `_id` that does not exist: stale token-derived ids, ids from another environment, or a visitor purged by GDPR/retention cleanup.","commonSituations":"Long-lived widget sessions after the visitor record was erased; imports that reference visitors never migrated; test data created against a different database.","solutions":["Verify the visitor exists (e.g. `GET /v1/livechat/visitor/:token`) before saving","If the visitor was removed, create a new visitor and re-associate instead of patching a dead id","Keep visitor ids server-resolved from the session token rather than client-supplied"],"exampleFix":"// before\nawait saveGuest({ _id: 'staleVisitor', name: 'John' }, userId);\n\n// after\nconst visitor = await LivechatVisitors.findOneById('staleVisitor', { projection: { _id: 1 } });\nif (!visitor) throw new Error('Visitor session expired — create a new visitor');\nawait saveGuest({ _id: visitor._id, name: 'John' }, userId);","handlingStrategy":"validation","validationCode":"const visitor = await LivechatVisitors.findOneById(visitorId, { projection: { _id: 1 } });\nif (!visitor) throw new Error('Visitor session no longer valid');\nawait saveGuest({ _id: visitor._id, ...updates }, userId);","typeGuard":"const visitorExists = async (id: string): Promise<boolean> =>\n  (await LivechatVisitors.findOneById(id, { projection: { _id: 1 } })) != null;","tryCatchPattern":"try {\n  await saveGuest(guestData, userId);\n} catch (e) {\n  if (e instanceof Error && e.message === 'error-invalid-visitor') {\n    // recreate visitor, rebind session, then retry save\n  }\n}","preventionTips":["Derive visitor ids from the session token server-side","Re-validate visitor after long-lived sessions (records can be purged)","Treat invalid-visitor as a session-reset signal in widgets"],"tags":["omnichannel","visitor","guest","not-found","livechat"],"backgroundTag":"entity-not-found","analyzedSha":"2a7de457074cbb4d4373fbd9a4e5bea292c9c764","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}