{"record":{"id":"9edf58c7a48e0f6a","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-contact-data","errorCode":"error-invalid-contact-data","errorMessage":"Invalid visitor token","messagePattern":"Invalid visitor token","errorType":"error_code","errorClass":"MeteorError","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/omnichannel/contacts/registerContact.ts","lineNumber":29,"sourceCode":"type RegisterContactProps = {\n\t_id?: string;\n\ttoken: string;\n\tname: string;\n\tusername?: string;\n\temail?: string;\n\tphone?: string;\n\tcustomFields?: Record<string, unknown | string>;\n\tcontactManager?: {\n\t\tusername: string;\n\t};\n};\n\nexport async function registerContact(\n\t{ token, name, email = '', phone, username, customFields = {}, contactManager }: RegisterContactProps,\n\tuserId: string,\n): Promise<string> {\n\tif (!token || typeof token !== 'string') {\n\t\tthrow new MeteorError('error-invalid-contact-data', 'Invalid visitor token');\n\t}\n\n\tconst visitorEmail = email.trim().toLowerCase();\n\n\tif (contactManager?.username) {\n\t\t// verify if the user exists with this username and has a livechat-agent role\n\t\tconst manager = await Users.findOneByUsername(contactManager.username, { projection: { roles: 1 } });\n\t\tif (!manager) {\n\t\t\tthrow new MeteorError('error-contact-manager-not-found', `No user found with username ${contactManager.username}`);\n\t\t}\n\t\tif (!manager.roles || !Array.isArray(manager.roles) || !manager.roles.includes('livechat-agent')) {\n\t\t\tthrow new MeteorError('error-invalid-contact-manager', 'The contact manager must have the role \"livechat-agent\"');\n\t\t}\n\t}\n\n\tconst existingUserByToken = await LivechatVisitors.getVisitorByToken(token, { projection: { _id: 1 } });\n\tlet visitorId = existingUserByToken?._id;\n","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/omnichannel/contacts/registerContact.ts#L11-L47","documentation":"registerContact validates the visitor token before doing anything else: a falsy or non-string token throws MeteorError('error-invalid-contact-data', 'Invalid visitor token'). The token is the visitor's stable cross-chat identifier, so it must be supplied as a non-empty string by the widget or integration.","triggerScenarios":"Calling the contact registration flow with token missing, empty string, null/undefined, or a non-string value in the payload; note the default parameter does not apply when the field is present but empty.","commonSituations":"Custom widgets forgetting to generate or persist the token; clients sending token as null explicitly; form-encoded payloads that parse to empty strings; schema drift between widget versions.","solutions":["Send a non-empty string token - typically a random id generated per visitor/device and reused thereafter","Validate the payload on the client before calling the API (token: non-empty string)","Keep using the same token across a visitor's sessions so conversations stay linked to one contact"],"exampleFix":"// before - token never sent -> 'Invalid visitor token'\nawait registerContact({ name: 'John', email: 'john@example.com' }, userId);\n\n// after - generate once per visitor and reuse\nconst token = existingToken || Random.id();\nawait registerContact({ token, name: 'John', email: 'john@example.com' }, userId);","handlingStrategy":"validation","validationCode":"// Widget/integration side, before calling registerContact:\nif (typeof token !== 'string' || token.trim().length === 0) {\n  token = Random.id(); // generate once per visitor, persist, and reuse thereafter\n}\nawait registerContact({ token, name, email }, userId);","typeGuard":"const isValidVisitorToken = (token: unknown): token is string =>\n  typeof token === 'string' && token.length > 0;","tryCatchPattern":"try {\n  await registerContact({ token, name, email }, userId);\n} catch (err: any) {\n  if (err?.error === 'error-invalid-contact-data') {\n    return badRequest('Invalid visitor token');\n  }\n  throw err;\n}","preventionTips":["Widgets must generate and persist a per-visitor token before any registration call","Validate the payload schema at the client boundary: token is a non-empty string","Reuse the token across sessions so all conversations link to the same contact"],"tags":["omnichannel","contacts","visitor","token","validation","meteor-error"],"backgroundTag":"invalid-visitor-token","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}