RocketChat/Rocket.Chat · error · Error

error-room-not-served

Error message

error-room-not-served

What it means

Thrown by resumeRoomOnHold (omnichannel.internalService.ts:101) when the on-hold room has no servedBy agent. Resuming means re-assigning the chat to its previous serving agent; if the room lost its agent (returned to queue, agent removed) there is nobody to hand it back to, so the service refuses.

Solutions

  1. Check room.servedBy before calling resumeRoomOnHold and route through the normal queue/assignment flow when it is absent
  2. Investigate why the on-hold room lost its serving agent (agent removal flow, routing manager) — on-hold rooms are expected to keep servedBy
  3. Repair the room record by re-assigning an agent (or clearing onHold) before retrying the resume

Example fix

// before
await omnichannelService.resumeRoomOnHold(room, comment, agent);

// after
if (!room.servedBy) {
	// no previous agent to resume with — fall back to queue routing
	return RoutingManager.takeQueue(sessionId);
}
await omnichannelService.resumeRoomOnHold(room, comment, agent);
Defensive patterns

Strategy: validation

Validate before calling

const room = await LivechatRooms.findOneById(rid, { projection: { t: 1, open: 1, onHold: 1, servedBy: 1 } });
if (!room?.servedBy) {
	// no previous agent — use queue assignment instead of resume
	return;
}
await omnichannelService.resumeRoomOnHold(room, comment, agent);

Type guard

const isServedRoom = (room: IOmnichannelRoom | null | undefined): room is IOmnichannelRoom & { servedBy: NonNullable<IOmnichannelRoom['servedBy']> } =>
	!!room && !!room.servedBy;

Try / catch

try {
	await omnichannelService.resumeRoomOnHold(room, comment, agent);
} catch (e) {
	if (e instanceof Error && e.message === 'error-room-not-served') {
		// re-assign via queue/routing instead of resume
	}
	throw e;
}

Prevention

When it happens

Trigger: Resuming an on-hold room whose servedBy is unset — the serving agent was removed while the chat was on hold, routing already stripped the agent, or the room is in an inconsistent served-less state.

Common situations: Agent went offline/was removed during the hold and a flow cleared servedBy; the conversation was queued again while flagged on hold; data inconsistency after manual DB edits or a partially failed routing operation.

Related errors


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

Appendix: source

Thrown at apps/meteor/ee/server/local-services/omnichannel.internalService.ts:101

		this.logger.debug({ msg: 'Attempting to resume room on hold', roomId: room._id, userId: resumeBy?._id });

		if (!room || !isOmnichannelRoom(room)) {
			throw new Error('error-invalid-room');
		}

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

		if (!room.onHold) {
			throw new Error('error-room-not-on-hold');
		}

		const { _id: roomId, servedBy } = room;

		if (!servedBy) {
			this.logger.error({ msg: 'No serving agent found for room', roomId });
			throw new Error('error-room-not-served');
		}

		const inquiry = await LivechatInquiry.findOneByRoomId(roomId, {});
		if (!inquiry) {
			this.logger.error({ msg: 'No inquiry found for room', roomId });
			throw new Error('error-invalid-inquiry');
		}

		await this.attemptToAssignRoomToServingAgentElseQueueIt({
			room,
			inquiry,
			servingAgent: servedBy,
			clientAction,
		});

		const [roomResult, subsResult] = await Promise.all([
			LivechatRooms.unsetOnHoldByRoomId(roomId),
			Subscriptions.unsetOnHoldByRoomId(roomId),

View on GitHub (pinned to b2c16d5842)