RocketChat/Rocket.Chat · error · Error
invalid-token
Error message
invalid-token
What it means
POST /api/v1/livechat/custom.field sets one custom field for a visitor identified by the body token. findGuest(token) maps to LivechatVisitors.getVisitorByToken(token); when no visitor document carries that token the endpoint throws 'invalid-token' before any field is written.
Solutions
- Use the exact token returned when the visitor was created (POST /api/v1/livechat/visitor returns visitor.token) in the same environment.
- Verify the token first with the visitor-info-by-token endpoint; if it no longer resolves, re-register the visitor and store the new token.
- Never reuse tokens across deployments; make the token part of per-environment configuration.
Example fix
// before
await post('/api/v1/livechat/custom.field', { token: staleToken, key: 'plan', value: 'pro' }); // invalid-token
// after
async function ensureGuest() {
const info = await get(`/api/v1/livechat/visitor.info/${token}`);
if (!info?.visitor) {
const { visitor } = await post('/api/v1/livechat/visitor', { visitor: { name: 'guest' } });
token = visitor.token; // persist this
}
return token;
}
await post('/api/v1/livechat/custom.field', { token: await ensureGuest(), key: 'plan', value: 'pro' }); Defensive patterns
Strategy: try-catch
Validate before calling
// Verify the visitor token before writing a custom field
const info = await get(`/api/v1/livechat/visitor.info/${token}`);
if (!info?.visitor) throw new Error('visitor token no longer valid; re-register guest');
await post('/api/v1/livechat/custom.field', { token, key, value, overwrite }); Try / catch
try {
await post('/api/v1/livechat/custom.field', { token, key, value });
} catch (e) {
if (e.message !== 'invalid-token') throw e;
const { visitor } = await post('/api/v1/livechat/visitor', { visitor: { name: 'guest' } });
token = visitor.token; // persist new token
await post('/api/v1/livechat/custom.field', { token, key, value });
} Prevention
- Store the token returned by visitor registration and never reuse tokens across environments.
- Pair the token write with the same lifecycle as the visitor record.
- Verify tokens after workspace resets/migrations.
When it happens
Trigger: POSTing livechat/custom.field with a token that was never registered, was mistyped, belongs to another environment (staging vs prod), or whose visitor document was deleted.
Common situations: Widget/integration persists an old token after the visitors collection was reset or the visitor re-registered; copy-paste between servers; token lost from localStorage so an empty/stale value is sent.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/d09933ae5f938c58.
Report an issue: GitHub.
Appendix: source
Thrown at apps/meteor/server/api/v1/omnichannel/customField.ts:30
validateUnauthorizedErrorResponse,
} from '@rocket.chat/rest-typings';
import { API } from '../..';
import { setCustomFields, setMultipleCustomFields } from '../../../lib/omnichannel/custom-fields';
import type { ExtractRoutesFromAPI } from '../../ApiClass';
import { findLivechatCustomFields, findCustomFieldById } from './lib/customFields';
import { findGuest } from './lib/livechat';
import { getPaginationItems } from '../../lib/getPaginationItems';
API.v1.addRoute(
'livechat/custom.field',
{ validateParams: isPOSTLivechatCustomFieldParams },
{
async post() {
const { token, key, value, overwrite } = this.bodyParams;
const guest = await findGuest(token);
if (!guest) {
throw new Error('invalid-token');
}
await setCustomFields({ token, key, value, overwrite });
return API.v1.success({ field: { key, value, overwrite } });
},
},
);
API.v1.addRoute(
'livechat/custom.fields',
{ validateParams: isPOSTLivechatCustomFieldsParams },
{
async post() {
const { token } = this.bodyParams;
const visitor = await findGuest(token);
if (!visitor) {
throw new Error('invalid-token');View on GitHub (pinned to b2c16d5842)