RocketChat/Rocket.Chat · error · Error
room-closed
room-closed
Error message
room-closed
What it means
Thrown in the POST handler of 'livechat/room.close' (room.ts:151-153) when room.open is false. After successfully finding the room by token and rid, the handler checks whether the room is still open. If the room has already been closed (by visitor, agent, or system), room.open is false and this error fires.
Source
Thrown at apps/meteor/server/api/v1/omnichannel/room.ts:152
async post() {
const { rid, token } = this.bodyParams;
if (!rcSettings.get('Omnichannel_allow_visitors_to_close_conversation')) {
throw new Error('error-not-allowed-to-close-conversation');
}
const visitor = await findGuest(token);
if (!visitor) {
throw new Error('invalid-token');
}
const room = await findRoom(token, rid);
if (!room) {
throw new Error('invalid-room');
}
if (!room.open) {
throw new Error('room-closed');
}
const language = rcSettings.get<string>('Language') || 'en';
const comment = i18n.t('Closed_by_visitor', { lng: language });
const options: CloseRoomParams['options'] = {};
if (room.servedBy) {
const servingAgent: Pick<IUser, '_id' | 'name' | 'username' | 'utcOffset' | 'settings' | 'language'> | null =
await Users.findOneById(room.servedBy._id, {
projection: {
name: 1,
username: 1,
utcOffset: 1,
settings: 1,
language: 1,
},
});
View on GitHub (pinned to f9d3ec372b)
Solutions
- Check the room's open status before attempting to close: db.livechat_rooms.findOne({_id: rid}, {open: 1}).
- If already closed, treat it as a no-op success — the desired end state is achieved.
- Disable the close button client-side after the first successful close to prevent double-submits.
Example fix
// before
await api.post('/livechat/room.close', { rid, token });
// throws 'room-closed' on second click
// after — guard against double-close client-side
if (roomAlreadyClosed) {
return { success: true, message: 'Room already closed' };
}
await api.post('/livechat/room.close', { rid, token }); Defensive patterns
Strategy: type-guard
Validate before calling
// Check room.open before attempting to close
const room = await LivechatRooms.findOneByIdAndVisitorToken(rid, token);
if (room && !room.open) {
// already closed — no action needed
return { success: true, reason: 'already-closed' };
} Type guard
function isRoomOpen(room: IOmnichannelRoom | null): room is IOmnichannelRoom {
return room !== null && room.open === true;
} Try / catch
try {
await api.post('/livechat/room.close', { rid, token });
} catch (err) {
if (err.message === 'room-closed') {
// already closed — treat as success
return { success: true };
}
} Prevention
- Check room.open status before calling room.close to avoid double-close.
- Disable the close button client-side immediately after the first click to prevent double-submits.
- Treat 'room-closed' as an idempotent success in client error handling.
When it happens
Trigger: Calling POST /api/v1/livechat/room.close for a room that is already in a closed state. This can happen on a double-close attempt or when an agent closed the room between the client's last status check and the close request.
Common situations: Visitor clicks close twice (double-submit); agent closed the room concurrently; the room was auto-closed by an inactivity timeout or system process; the client didn't refresh room state after a previous close.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/298e9a52e1f672c0.
Report an issue: GitHub.