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
- Check room.servedBy before calling resumeRoomOnHold and route through the normal queue/assignment flow when it is absent
- Investigate why the on-hold room lost its serving agent (agent removal flow, routing manager) — on-hold rooms are expected to keep servedBy
- 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
- Guard resume flows with a servedBy check and fall back to queue routing
- Keep on-hold rooms' servedBy intact when removing agents, or clear onHold at the same time
- Monitor logs for 'No serving agent found for room' to catch inconsistent rooms early
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)