{"record":{"id":"293920c20463efc7","repo":"RocketChat/Rocket.Chat","slug":"visitor-with-id-visitorid-not-found","errorCode":null,"errorMessage":"Visitor with id ${visitorId} not found","messagePattern":"Visitor with id (.+?) not found","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"apps/meteor/app/apps/server/bridges/listeners.ts","lineNumber":466,"sourceCode":"\t\t\t\t}\n\n\t\t\t\treturn this.orch\n\t\t\t\t\t.getManager()\n\t\t\t\t\t.getListenerManager()\n\t\t\t\t\t.executeListener(args.event, {\n\t\t\t\t\t\troom,\n\t\t\t\t\t\tfrom: from as NonNullable<typeof from>, // type definition in the apps-engine seems to be incorrect\n\t\t\t\t\t\tto,\n\t\t\t\t\t\ttype: transferData.type,\n\t\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase AppInterface.IPostLivechatGuestSaved: {\n\t\t\t\tconst [visitorId] = args.payload;\n\t\t\t\tconst visitor = await this.orch.getConverters().get('visitors').convertById(visitorId);\n\n\t\t\t\tif (!visitor) {\n\t\t\t\t\tthrow new Error(`Visitor with id ${visitorId} not found`);\n\t\t\t\t}\n\n\t\t\t\treturn this.orch.getManager().getListenerManager().executeListener(args.event, visitor);\n\t\t\t}\n\n\t\t\tcase AppInterface.IPostLivechatRoomSaved: {\n\t\t\t\tconst [roomId] = args.payload;\n\t\t\t\tconst room = await this.orch.getConverters().get('rooms').convertById(roomId);\n\n\t\t\t\tif (!room) {\n\t\t\t\t\tthrow new Error(`Room with id ${roomId} not found`);\n\t\t\t\t}\n\n\t\t\t\treturn this.orch\n\t\t\t\t\t.getManager()\n\t\t\t\t\t.getListenerManager()\n\t\t\t\t\t.executeListener(args.event, room as IAppsLivechatRoom);\n\t\t\t}","sourceCodeStart":448,"sourceCodeEnd":484,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/app/apps/server/bridges/listeners.ts#L448-L484","documentation":"Thrown by the livechatEvent handler in the apps-engine bridge when an IPostLivechatGuestSaved event fires with a visitorId, but the visitors converter's convertById returns null/undefined. The bridge refuses to dispatch the event to registered App listeners because the payload object cannot be materialized. It indicates the visitor record is gone or never existed by the time the after-save hook runs.","triggerScenarios":"Server emits IPostLivechatGuestSaved with a visitorId; this.orch.getConverters().get('visitors').convertById(visitorId) resolves to a falsy value (the LivechatVisitors document was deleted, not yet replicated, or the id is stale between event enqueue and processing).","commonSituations":"A migration or purge job deletes visitors concurrently with guest-save events; a custom integration fires IPostLivechatGuestSaved manually with a wrong id; replication lag in a sharded Mongo setup; an App's own before-hook removed the visitor before the after-hook converted it.","solutions":["Verify the visitor still exists in the LivechatVisitors collection at the moment the event is processed (race with a concurrent delete/purge).","If you emit IPostLivechatGuestSaved from custom code, ensure the visitor document is committed before dispatching the payload id.","Audit any visitor-deletion path (GDPR purge, omni-core cleanup) that may run between save and event processing and serialize it after event dispatch.","Check Mongo/oplog replication health if this appears intermittently in a clustered deployment."],"exampleFix":"// before: visitor deleted before after-save hook converts it\nawait LivechatVisitors.removeById(visitorId);\nawait emitEvent(IPostLivechatGuestSaved, { payload: [visitorId] }); // convertById now fails\n\n// after: dispatch event first, then remove (or skip dispatch for removed records)\nawait emitEvent(IPostLivechatGuestSaved, { payload: [visitorId] });\nawait LivechatVisitors.removeById(visitorId);","handlingStrategy":"try-catch","validationCode":"// The throw happens inside the bridge before the App listener runs,\n// so the App cannot pre-validate. Ensure the visitor exists when the\n// event is emitted:\nconst visitor = await LivechatVisitors.findOneById(visitorId);\nif (!visitor) {\n  // skip emitting IPostLivechatGuestSaved for a non-existent visitor\n  return;\n}\nawait emitEvent(AppInterface.IPostLivechatGuestSaved, { payload: [visitorId] });","typeGuard":"const isPersistedVisitorId = async (id: string): Promise<boolean> => {\n  return Boolean(await LivechatVisitors.findOneById(id, { projection: { _id: 1 } }));\n};","tryCatchPattern":"// Framework-level: the apps-engine swallows and logs this throw.\n// In a custom bridge wrapper, catch and degrade gracefully:\ntry {\n  await listenerManager.executeListener(args.event, visitor);\n} catch (e) {\n  this.orch.debugLog(`Skipping ${args.event}: ${(e as Error).message}`);\n}","preventionTips":["Do not delete visitors in a path that also emits IPostLivechatGuestSaved.","Serialize purges/GDPR deletions to run after livechat events drain.","When emitting the event from custom code, confirm the visitor document is committed first."],"tags":["apps-engine","livechat","event-listener","data-integrity","race-condition"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}