RocketChat/Rocket.Chat · error · MeteorError
error-invalid-contact-data
error-invalid-contact-data
Error message
Invalid visitor token
What it means
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.
Source
Thrown at apps/meteor/server/lib/omnichannel/contacts/registerContact.ts:29
type RegisterContactProps = {
_id?: string;
token: string;
name: string;
username?: string;
email?: string;
phone?: string;
customFields?: Record<string, unknown | string>;
contactManager?: {
username: string;
};
};
export async function registerContact(
{ token, name, email = '', phone, username, customFields = {}, contactManager }: RegisterContactProps,
userId: string,
): Promise<string> {
if (!token || typeof token !== 'string') {
throw new MeteorError('error-invalid-contact-data', 'Invalid visitor token');
}
const visitorEmail = email.trim().toLowerCase();
if (contactManager?.username) {
// verify if the user exists with this username and has a livechat-agent role
const manager = await Users.findOneByUsername(contactManager.username, { projection: { roles: 1 } });
if (!manager) {
throw new MeteorError('error-contact-manager-not-found', `No user found with username ${contactManager.username}`);
}
if (!manager.roles || !Array.isArray(manager.roles) || !manager.roles.includes('livechat-agent')) {
throw new MeteorError('error-invalid-contact-manager', 'The contact manager must have the role "livechat-agent"');
}
}
const existingUserByToken = await LivechatVisitors.getVisitorByToken(token, { projection: { _id: 1 } });
let visitorId = existingUserByToken?._id;
View on GitHub (pinned to b2c16d5842)
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
Example fix
// before - token never sent -> 'Invalid visitor token'
await registerContact({ name: 'John', email: 'john@example.com' }, userId);
// after - generate once per visitor and reuse
const token = existingToken || Random.id();
await registerContact({ token, name: 'John', email: 'john@example.com' }, userId); Defensive patterns
Strategy: validation
Validate before calling
// Widget/integration side, before calling registerContact:
if (typeof token !== 'string' || token.trim().length === 0) {
token = Random.id(); // generate once per visitor, persist, and reuse thereafter
}
await registerContact({ token, name, email }, userId); Type guard
const isValidVisitorToken = (token: unknown): token is string => typeof token === 'string' && token.length > 0;
Try / catch
try {
await registerContact({ token, name, email }, userId);
} catch (err: any) {
if (err?.error === 'error-invalid-contact-data') {
return badRequest('Invalid visitor token');
}
throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- error-invalid-contact
- error-invalid-token
- error-contact-manager-not-found
- error-invalid-contact-manager
- error-contact-not-found
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/9edf58c7a48e0f6a.
Report an issue: GitHub.