RocketChat/Rocket.Chat · error · Error

invalid-token

Error message

invalid-token

What it means

Thrown by GET /api/v1/livechat/room when the visitor lookup fails. Because the handler first runs check(this.queryParams, { token: String, rid: Match.Maybe(String), ... }) (extensible via the onCheckRoomParams patch point), a missing token raises a Match validation error instead — so 'invalid-token' here means the token value matched no LivechatVisitors document, or an injected onCheckRoomParams hook made token optional.

Solutions

  1. Register the visitor first (POST /api/v1/livechat/visitor) or let the widget's registration flow complete, then call livechat/room with the issued token.
  2. Confirm the token survives storage round-trips (no whitespace/newlines) and matches the environment.
  3. If you extended onCheckRoomParams, keep token required in the Match pattern so missing tokens fail validation with a clearer message.

Example fix

// before (room requested with a fabricated token)
await fetch(`/api/v1/livechat/room?token=my-invented-token`);

// after (register visitor, then create/obtain the room)
const v = await (await fetch('/api/v1/livechat/visitor', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ visitor: { token: 'tkn1', name: 'Lead' } }) })).json();
await fetch(`/api/v1/livechat/room?token=${v.visitor.token}`);
Defensive patterns

Strategy: validation

Validate before calling

// livechat/room does NOT register visitors — register first
await ensureRegisteredVisitor({ token });
const res = await fetch(`/api/v1/livechat/room?token=${encodeURIComponent(token)}`);

Try / catch

if (isLivechatErrorResponse(body) && body.error === 'invalid-token') {
  await ensureRegisteredVisitor({ token }); // register then retry room creation once
}

Prevention

When it happens

Trigger: GET /api/v1/livechat/room?token=<unknown> (optionally &rid=&agentId=). Known trigger sub-case: relying on this endpoint to auto-create a room but passing a token that was never registered.

Common situations: Integration calls livechat/room before registering the visitor — the endpoint does NOT register visitors (unlike livechat/messages); widget token lost from storage; Apps/custom code extending onCheckRoomParams loosening the token requirement and hitting the null path.

Related errors


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

Appendix: source

Thrown at apps/meteor/server/api/v1/omnichannel/room.ts:77

			intervalTimeInMS: 60000,
		},
	},
	{
		async get() {
			// I'll temporary use check for validation, as validateParams doesnt support what's being done here
			const extraCheckParams = onCheckRoomParams({
				token: String,
				rid: Match.Maybe(String),
				agentId: Match.Maybe(String),
			});

			check(this.queryParams, extraCheckParams);

			const { token, rid, agentId, ...extraParams } = this.queryParams;

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

			if (!rid) {
				const room = await LivechatRooms.findOneOpenByVisitorToken(token, {});
				if (room) {
					return API.v1.success({ room, newRoom: false });
				}

				let agent: SelectedAgent | undefined;
				const agentObj = agentId && (await findAgent(agentId));
				if (agentObj) {
					if (isAgentWithInfo(agentObj)) {
						const { username = undefined } = agentObj;
						agent = { agentId, username };
					} else {
						agent = { agentId };
					}
				}

View on GitHub (pinned to b2c16d5842)