RocketChat/Rocket.Chat · error · Error

invalid-token

Error message

invalid-token

What it means

Thrown by POST /livechat/message (message.ts:31-34) when findGuest(token) returns null — the visitor token in the request body does not resolve to a visitor. This is the first guard in the message-send flow (before room lookup). Returns HTTP 400 { success:false, error:'invalid-token' }.

Source

Thrown at apps/meteor/server/api/v1/omnichannel/message.ts:33

import { findGuest, findRoom, normalizeHttpHeaderData } from './lib/livechat';
import { callbacks } from '../../../lib/callbacks';
import { loadMessageHistory } from '../../../lib/messages/loadMessageHistory';
import { updateMessage, deleteMessage, sendMessage } from '../../../lib/omnichannel/messages';
import { normalizeMessageFileUpload } from '../../../lib/utils/functions/normalizeMessageFileUpload';
import { settings } from '../../../settings';
import { getPaginationItems } from '../../lib/getPaginationItems';
import { isWidget } from '../../lib/isWidget';

API.v1.addRoute(
	'livechat/message',
	{ validateParams: isPOSTLivechatMessageParams },
	{
		async post() {
			const { token, rid, agent, msg } = this.bodyParams;

			const guest = await findGuest(token);
			if (!guest) {
				throw new Error('invalid-token');
			}

			const room = await findRoom(token, rid);
			if (!room) {
				throw new Error('invalid-room');
			}

			if (!room.open) {
				throw new Error('room-closed');
			}

			if (
				settings.get('Livechat_enable_message_character_limit') &&
				msg.length > parseInt(settings.get('Livechat_message_character_limit'))
			) {
				throw new Error('message-length-exceeds-character-limit');
			}

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Resolve/refresh the visitor token via /livechat/visitor before sending messages.
  2. Send the visitor `token`, not the `_id`.
  3. If the visitor is gone, re-register to obtain a fresh token and room.

Example fix

// before
POST /livechat/message { token: stale, rid, msg }

// after
const guest = await findGuest(token);
if (!guest) { /* re-register visitor, then retry */ }
POST /livechat/message { token: guest.token, rid, msg }
Defensive patterns

Strategy: validation

Validate before calling

const guest = await findGuest(token);
if (!guest) {
  // re-register visitor, then retry the send
  throw new Error('visitor token invalid');
}
// safe to POST /livechat/message

Type guard

const isGuest = (g: unknown): g is ILivechatVisitor =>
  !!g && typeof g === 'object' && typeof (g as any).token === 'string';

Try / catch

try { await sendMessage({ token, rid, msg }); }
catch (e) { if (e instanceof Error && e.message === 'invalid-token') { /* refresh token, retry once */ } else throw e; }

Prevention

When it happens

Trigger: POST /livechat/message with an unknown, stale, typo'd, or empty `token` in the body.

Common situations: Widget token expired/purged; visitor re-registered with a new token; sending `_id` instead of `token`.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12). Data as JSON: /api/errors/1d44e3de93cfb9de. Report an issue: GitHub.