RocketChat/Rocket.Chat · error · Meteor.Error
error-invalid-token
error-invalid-token
Error message
Token cannot be empty
What it means
Thrown by POST /api/v1/livechat/visitor (the widget's visitor registration endpoint, no auth required) when bodyParams.visitor.token is missing, empty, or whitespace-only. The token is the visitor's client-generated identity across chats; the endpoint explicitly rejects tokenless payloads with code error-invalid-token and details {method: 'livechat/visitor'}.
Solutions
- Generate and persist a unique token client-side (e.g. random 43-char string) and send it inside visitor: {"visitor":{"token":"iNKE7a6k6c2qKxtX","name":"John"}}
- Keep using the same token for return visitors so their conversation history is linked
- Guard with a trim() check before posting so the user never sees the 400
Example fix
// before
await fetch(`${server}/api/v1/livechat/visitor`, { method: 'POST', body: JSON.stringify({ visitor: { name } }) });
// after
const token = localStorage.rcToken || (localStorage.rcToken = crypto.randomUUID().replace(/-/g, ''));
await fetch(`${server}/api/v1/livechat/visitor`, { method: 'POST', body: JSON.stringify({ visitor: { token, name } }) }); Defensive patterns
Strategy: validation
Validate before calling
const token = getStoredVisitorToken() ?? (setStoredVisitorToken(randomToken()), getStoredVisitorToken());
if (!token?.trim()) throw new Error('visitor token missing');
await fetch(`${server}/api/v1/livechat/visitor`, { method: 'POST', body: JSON.stringify({ visitor: { token, ...profile } }) }); Type guard
const hasVisitorToken = (v: unknown): v is { token: string } =>
typeof v === 'object' && v !== null && typeof (v as any).token === 'string' && (v as any).token.trim().length > 0; Try / catch
const body = await (await fetch(url, { method: 'POST', body })).json();
if (!body.success && body.error === 'Token cannot be empty') { /* generate token, persist, re-register */ } Prevention
- Generate and persist the token in localStorage BEFORE first registration
- Reuse one token per end user so conversations link correctly
- Never send registration payloads without the nested visitor object
When it happens
Trigger: POST /api/v1/livechat/visitor with {"visitor":{}} or {"visitor":{"name":"John"}} (no token), token: '' or token: ' '. Also sending the payload as {"token":"..."} at the top level instead of nested under visitor.
Common situations: Custom widget implementations that forget to generate/persist a token before registering; headless API tests that reuse a template body and drop the token field; integrations that clear localStorage (where the widget keeps the token) between sessions and send a fresh-but-empty value.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- invalid-token
- error-invalid-contact-data
- error-livechat-visitor-registration
- error-id-param-not-provided
- error-invalid-arguments
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/b0c1a6c810845978.
Report an issue: GitHub.
Appendix: source
Thrown at apps/meteor/server/api/v1/omnichannel/visitor.ts:47
name: Match.Maybe(String),
email: Match.Maybe(String),
department: Match.Maybe(String),
phone: Match.Maybe(String),
username: Match.Maybe(String),
customFields: Match.Maybe([
Match.ObjectIncluding({
key: String,
value: String,
overwrite: Boolean,
}),
]),
}),
});
const { customFields, id, token, name, email, department, phone, username, connectionData } = this.bodyParams.visitor;
if (!token?.trim()) {
throw new Meteor.Error('error-invalid-token', 'Token cannot be empty', { method: 'livechat/visitor' });
}
const guest = {
token,
...(id && { id }),
...(name && { name }),
...(email && { email }),
...(department && { department }),
...(username && { username }),
...(connectionData && { connectionData }),
...(phone && typeof phone === 'string' && { phone: { number: phone as string } }),
connectionData: normalizeHttpHeaderData(this.request.headers),
};
const visitor = await registerGuest(guest, {
shouldConsiderIdleAgent: settings.get<boolean>('Livechat_enabled_when_agent_idle'),
shouldConsiderOfflineAgent: settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
});View on GitHub (pinned to b2c16d5842)