RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-user

error-invalid-user

Error message

error-invalid-user

What it means

After the state and MAC checks, returnRoomAsInquiry loads the serving agent with Users.findOneById(room.servedBy._id). If that user no longer exists in the users collection, it throws Meteor.Error('error-invalid-user') — the room references an agent that was deleted.

Source

Thrown at apps/meteor/server/lib/omnichannel/rooms.ts:240

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

	if (room.onHold) {
		throw new Meteor.Error('error-room-onHold');
	}

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

	if (!room.servedBy) {
		return false;
	}

	const user = await Users.findOneById(room.servedBy._id);
	if (!user?._id) {
		throw new Meteor.Error('error-invalid-user');
	}

	const inquiry = await LivechatInquiry.findOne({ rid: room._id });
	if (!inquiry) {
		return false;
	}

	// update inquiry's last message with room's last message to correctly display in the queue
	// because we stop updating the inquiry when it's been taken
	if (room.lastMessage) {
		await LivechatInquiry.setLastMessageById(inquiry._id, room.lastMessage);
	}

	const transferredBy = normalizeTransferredByData(user, room);
	livechatLogger.debug({ msg: 'Transferring room by user', roomId: room._id, transferredBy: transferredBy._id });
	const transferData = { scope: 'queue' as const, departmentId, transferredBy, ...overrideTransferData };
	try {
		await saveTransferHistory(room, transferData);

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Restore or re-create the agent user, or clear/reassign room.servedBy before returning the room.
  2. Audit for orphan rooms (servedBy._id missing from users) after any bulk user deletion.
  3. Only delete users after their open omnichannel rooms are closed or transferred.

Example fix

// before
await returnRoomAsInquiry(room, departmentId); // servedBy agent deleted -> error-invalid-user

// after
const agent = room.servedBy ? await Users.findOneById(room.servedBy._id, { projection: { _id: 1 } }) : null;
if (!agent) {
	// reassign or clear the stale agent reference first
	await LivechatRooms.unsetAgentByRoomId(room._id);
}
await returnRoomAsInquiry(room, departmentId);
Defensive patterns

Strategy: validation

Validate before calling

const agent = room.servedBy
	? await Users.findOneById(room.servedBy._id, { projection: { _id: 1 } })
	: null;
if (!agent) {
	// reassign or clear the stale servedBy reference before returning the room
}

Type guard

const hasLiveServingAgent = async (room: Pick<IOmnichannelRoom, 'servedBy'>): Promise<boolean> =>
	Boolean(room.servedBy && (await Users.findOneById(room.servedBy._id, { projection: { _id: 1 } })));

Try / catch

try {
	await returnRoomAsInquiry(room, departmentId);
} catch (err) {
	if (err instanceof Meteor.Error && err.error === 'error-invalid-user') {
		// serving agent is gone: repair room.servedBy or close the orphan room
		return;
	}
	throw err;
}

Prevention

When it happens

Trigger: Returning a room to the queue when the agent recorded in room.servedBy has been deleted from the database, leaving the reference dangling.

Common situations: Agent user hard-deleted while still serving chats; imported/restored database with orphan rooms; user-deletion cascades that skip livechat room cleanup.

Related errors


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