RocketChat/Rocket.Chat · error · Error
This_conversation_is_already_closed
Error message
This_conversation_is_already_closed
What it means
Thrown by resumeRoomOnHold when `room.open` is false. You cannot resume a held chat that is already closed, because closing terminates the conversation. This guard precedes the onHold check.
Source
Thrown at apps/meteor/ee/server/local-services/omnichannel.internalService.ts:90
}
await callbacks.run('livechat:afterOnHold', room);
}
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');
}View on GitHub (pinned to f9d3ec372b)
Solutions
- Filter the held-chats list by open === true so closed rooms never offer a Resume action.
- Refresh the room before resume and tell the user the chat is closed if room.open is false.
- Check the close-on-hold timeout settings if held rooms are closing unexpectedly.
Example fix
// before
await omnichannelService.resumeRoomOnHold(room, comment, user);
// after
if (!room.open) {
throw new Error('This conversation is already closed');
}
await omnichannelService.resumeRoomOnHold(room, comment, user); Defensive patterns
Strategy: validation
Validate before calling
if (!room.open) throw new Error('This conversation is already closed');
await omnichannelService.resumeRoomOnHold(room, comment, user); Type guard
function isOpenRoom(room: unknown): boolean {
return !!room && (room as any).open === true;
} Try / catch
try {
await omnichannelService.resumeRoomOnHold(room, comment, user);
} catch (e) {
if (e instanceof Error && e.message === 'This_conversation_is_already_closed') {
notifyUser('This conversation is already closed.');
return;
}
throw e;
} Prevention
- Filter the held-chats list by open === true.
- Refresh the room before resume.
- Tune close-on-hold timeouts if held rooms close unexpectedly.
When it happens
Trigger: Calling resumeRoomOnHold for a room whose `open` is false — a chat that was closed (agent closed it, visitor left, abandonment timeout) while it happened to also be on hold.
Common situations: A held chat expires and auto-closes, then an agent clicks Resume; the UI still lists the held chat after a close event; concurrent close + resume.
Related errors
- error-room-already-closed
- This_conversation_is_already_closed
- error-invalid-room
- error-invalid-sla
- error-forwarding-department-target-not-allowed
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/ff0c04bcf762e82a.
Report an issue: GitHub.