RocketChat/Rocket.Chat · error · Error
error-not-allowed-to-close-conversation
Error message
error-not-allowed-to-close-conversation
What it means
Thrown by POST /api/v1/livechat/room.close when the server setting Omnichannel_allow_visitors_to_close_conversation is false. This is a configuration gate evaluated before any token/room validation — visitors are simply not permitted to end conversations through this endpoint. Admins/agents must instead use livechat/room.closeByUser, which is user-authenticated.
Solutions
- Enable 'Allow visitors to close conversations' (Omnichannel_allow_visitors_to_close_conversation) in Administration > Workspace > Omnichannel settings if visitors should close chats.
- If closure must stay agent-only, hide the visitor close button and use POST /api/v1/livechat/room.closeByUser with an authenticated user and hasPermission for closing.
- Have the client read the livechat init settings (visitorsCanCloseChat in GET livechat/config) and only show the close affordance when true.
Example fix
// before (close button always shown)
<button onClick={() => sdk.post('livechat/room.close', { rid, token })}>End chat</button>
// after (respect the server setting surfaced via livechat config)
const { settings } = await (await fetch('/api/v1/livechat/config')).json();
{settings.visitorsCanCloseChat && (
<button onClick={() => sdk.post('livechat/room.close', { rid, token })}>End chat</button>
)} Defensive patterns
Strategy: validation
Validate before calling
// Read the capability from livechat config before showing the close button
const cfg = await (await fetch('/api/v1/livechat/config')).json();
const visitorsCanClose: boolean = cfg.settings?.visitorsCanCloseChat === true;
if (visitorsCanClose) renderCloseButton(); Try / catch
if (isLivechatErrorResponse(body) && body.error === 'error-not-allowed-to-close-conversation') {
hideCloseButton(); // server forbids visitor closure; do not retry
showNotice('This chat can only be ended by an agent.');
} Prevention
- Gate the UI on the config value (visitorsCanCloseChat), not on a hardcoded assumption.
- If your product requires visitor closure, verify the admin setting during deployment smoke tests.
- For agent-side closure, use the authenticated livechat/room.closeByUser route instead.
When it happens
Trigger: POST /api/v1/livechat/room.close with { rid, token } on a workspace where the setting is disabled (Administration > Omnichannel > 'Allow visitors to close conversations' off). It fires regardless of token validity, since the check comes first.
Common situations: Fresh installs or locked-down enterprise workspaces where admins deliberately keep closure agent-controlled; a widget shipped with a 'close chat' button deployed against a server that disallows visitor closure; settings reset during migration.
Related errors
- message-length-exceeds-character-limit
- agent-not-found
- error-comment-is-required
- error-forwarding-chat
- error-invalid-department-unit
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/50fdba0a7a2ecea7.
Report an issue: GitHub.
Appendix: source
Thrown at apps/meteor/server/api/v1/omnichannel/room.ts:138
throw new Error('invalid-room');
}
return API.v1.success({ room: froom, newRoom: false });
},
},
);
// Note: use this route if a visitor is closing a room
// If a RC user(like eg agent) is closing a room, use the `livechat/room.closeByUser` route
API.v1.addRoute(
'livechat/room.close',
{ validateParams: isPOSTLivechatRoomCloseParams },
{
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 });View on GitHub (pinned to b2c16d5842)