RocketChat/Rocket.Chat · error · Error

error-invalid-visitor

error-invalid-visitor

Error message

error-invalid-visitor

What it means

Thrown by POST livechat/room.forward when LivechatVisitors.findOneEnabledById(room.v._id) returns null. The visitor referenced by the room either does not exist or is disabled (archived/deleted via visitor management). Without a live guest record the transfer cannot proceed because guest identity is required for routing and audit.

Source

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

				transferredTo?: { _id: string; username?: string; name?: string };
			};

			const room = await LivechatRooms.findOneById(this.bodyParams.roomId);
			if (room?.t !== 'l') {
				throw new Error('error-invalid-room');
			}

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

			if (!(await Omnichannel.isWithinMACLimit(room))) {
				throw new Error('error-mac-limit-reached');
			}

			const guest = await LivechatVisitors.findOneEnabledById(room.v?._id);
			if (!guest) {
				throw new Error('error-invalid-visitor');
			}

			transferData.transferredBy = normalizeTransferredByData(this.user, room);
			if (transferData.userId) {
				const userToTransfer = await Users.findOneById(transferData.userId);
				if (userToTransfer) {
					transferData.transferredTo = {
						_id: userToTransfer._id,
						username: userToTransfer.username,
						name: userToTransfer.name,
					};
				}
			}

			const chatForwardedResult = await transfer(room, guest, transferData);
			if (!chatForwardedResult) {
				throw new Error('error-forwarding-chat');
			}

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Verify the visitor exists and is enabled via GET livechat/visitors.info?token=... or by querying LivechatVisitors directly.
  2. If the visitor was archived, restore it before retrying the forward.
  3. Close the orphaned room (livechat/room.close) and start a new conversation rather than transferring a broken one.
  4. Audit compliance/cleanup jobs that delete visitors to also close their open rooms.

Example fix

// before
await POST('/api/v1/livechat/room.forward', { roomId, userId });

// after
const visitor = await LivechatVisitors.findOneEnabledById(room.v?._id);
if (!visitor) {
  await POST('/api/v1/livechat/room.close', { roomId, comment: 'visitor missing' });
  return;
}
await POST('/api/v1/livechat/room.forward', { roomId, userId });
Defensive patterns

Strategy: validation

Validate before calling

const visitor = await LivechatVisitors.findOneEnabledById(room.v?._id);
if (!visitor) throw new ClientError('visitor-disabled-or-missing');

Type guard

const hasEnabledVisitor = (room: { v?: { _id?: string } } | null): room is { v: { _id: string } } =>
  !!room?.v?._id;

Try / catch

try {
  await POST('/api/v1/livechat/room.forward', { roomId, userId });
} catch (e) {
  if (e.message === 'error-invalid-visitor') { closeOrphanedRoom(roomId); return; }
  throw e;
}

Prevention

When it happens

Trigger: POST livechat/room.forward on a room whose v._id points to a visitor that was deleted, archived, or had its enabled flag cleared by a compliance/privacy job. Also occurs if the room document is orphaned (visitor removed manually from the DB).

Common situations: GDPR/cleanup scripts that delete visitors but leave rooms open; LDAP/SSO user purges that cascade to visitors; manual DB surgery that broke the room.visitor link; a migration that recreated visitors with new _ids.

Related errors


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