RocketChat/Rocket.Chat · error · Error
error-room-not-on-hold
Error message
error-room-not-on-hold
What it means
Thrown by resumeRoomOnHold when `room.onHold` is false. Resume is only meaningful for a held chat; calling it on a chat that is not on hold is a no-op that the service rejects. The check runs after the open check and before reading servedBy.
Source
Thrown at apps/meteor/ee/server/local-services/omnichannel.internalService.ts:94
async resumeRoomOnHold(
room: Pick<IOmnichannelRoom, '_id' | 't' | 'open' | 'onHold' | 'servedBy'>,
comment: string,
resumeBy: Pick<IUser, '_id' | 'username' | 'name'>,
clientAction = false,
) {
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,View on GitHub (pinned to f9d3ec372b)
Solutions
- Drive the Resume button off room.onHold === true and disable it after the first click.
- Re-fetch the room before resume and treat onHold === false as 'already active' rather than erroring.
- Listen for the room-changed notification that flips onHold and update the UI immediately.
Example fix
// before
await omnichannelService.resumeRoomOnHold(room, comment, user);
// after
if (!room.onHold) {
return { alreadyActive: true };
}
await omnichannelService.resumeRoomOnHold(room, comment, user); Defensive patterns
Strategy: validation
Validate before calling
if (!room.onHold) return { alreadyActive: true };
await omnichannelService.resumeRoomOnHold(room, comment, user); Type guard
function isHeldRoom(room: unknown): boolean {
return !!room && (room as any).onHold === true;
} Try / catch
try {
await omnichannelService.resumeRoomOnHold(room, comment, user);
} catch (e) {
if (e instanceof Error && e.message === 'error-room-not-on-hold') return;
throw e;
} Prevention
- Drive Resume off room.onHold === true and disable after the first click.
- Treat onHold === false as 'already active'.
- Listen for room-change notifications to keep onHold state fresh.
When it happens
Trigger: Calling resumeRoomOnHold on a room whose onHold is false (an active or queued chat that was never held, or one already resumed by another action).
Common situations: Double resume (user clicks Resume twice); a held room was auto-resumed by a visitor message and the agent's stale UI still shows it as held.
Related errors
- error-room-is-already-on-hold
- error-room-already-closed
- error-unserved-rooms-cannot-be-placed-onhold
- This_conversation_is_already_closed
- This_conversation_is_already_closed
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/105cb716d6d4f2e4.
Report an issue: GitHub.