RocketChat/Rocket.Chat · error · Meteor.Error

invalid-token

invalid-token

Error message

invalid-token

What it means

The sendMessageLivechat Meteor method (deprecated since 9.0.0 in favor of /v1/livechat/message) authenticates the sender purely by visitor token: LivechatVisitors.getVisitorByToken(token) found no matching document, so the send is rejected with invalid-token. The token must belong to an existing livechat visitor on this workspace.

Source

Thrown at apps/meteor/server/meteor-methods/omnichannel/sendMessageLivechat.ts:56

	check(
		agent,
		Match.Maybe({
			agentId: String,
			username: String,
		}),
	);

	const guest = await LivechatVisitors.getVisitorByToken(token, {
		projection: {
			name: 1,
			username: 1,
			department: 1,
			token: 1,
		},
	});

	if (!guest) {
		throw new Meteor.Error('invalid-token');
	}

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

	return sendMessage({
		guest,
		message: {
			_id,
			rid,
			msg,
			token,
			file,
			files,
			attachments,
		},
		agent,

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Register (or restore) the livechat visitor before sending and use the token returned by that registration for all sends
  2. Clear the stale guest token in the widget (localStorage) so a new visitor and token are generated
  3. Confirm the token comes from the same workspace/environment — tokens do not transfer across servers

Example fix

// before
Meteor.call('sendMessageLivechat', { token: staleToken, _id, rid, msg });

// after
const { visitor } = await registerLivechatVisitor(); // returns a valid token
Meteor.call('sendMessageLivechat', { token: visitor.token, _id, rid, msg });
Defensive patterns

Strategy: retry

Validate before calling

const info = await fetch(`/api/v1/livechat/visitor.info?token=${encodeURIComponent(token)}`);
if (!info.ok) {
  const reg = await registerLivechatVisitor();
  token = reg.token; // refresh the dead token before sending
}

Type guard

const isInvalidToken = (e: unknown): e is Meteor.Error =>
  typeof e === 'object' && e !== null && (e as { error?: string }).error === 'invalid-token';

Try / catch

try {
  await send(token);
} catch (e) {
  if (isInvalidToken(e)) {
    token = (await registerLivechatVisitor()).token; // acquire a fresh, valid visitor identity
    return send(token); // one retry with the new token
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling sendMessageLivechat with a message whose token was never registered, belongs to another workspace/environment, or whose visitor was deleted; a stale token persisted in the widget's localStorage after a data reset or server migration.

Common situations: Livechat widget reusing an old localStorage token after the server was reinstalled or visitors purged; dev vs prod token mixups; hand-rolled integrations that invent tokens instead of registering a visitor first.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/b3249f0b0286e81f. Report an issue: GitHub.