RocketChat/Rocket.Chat · error · MeteorError
room-closed
room-closed
Error message
Room is closed
What it means
During join(), omnichannel rooms that are not open (room.open !== true, i.e. the conversation is closed) are rejected with Meteor error room-closed ('Room is closed', method 'joinRoom'). Closed omnichannel conversations cannot be re-joined as members; they must be reopened through the omnichannel flow.
Source
Thrown at apps/meteor/server/services/room/service.ts:190
sendMessage = true,
): Promise<void> {
await saveRoomTopic(roomId, roomTopic, user, sendMessage);
}
async getRouteLink(room: AtLeast<IRoom, '_id' | 't' | 'name'>): Promise<string | boolean> {
return roomCoordinator.getRouteLink(room.t as string, { rid: room._id, name: room.name });
}
/**
* Method called by users to join a room.
*/
async join({ room, user, joinCode }: { room: IRoom; user: IUser; joinCode?: string }) {
if (!(await roomCoordinator.getRoomDirectives(room.t)?.allowMemberAction(room, RoomMemberActions.JOIN, user._id))) {
throw new MeteorError('error-not-allowed', 'Not allowed', { method: 'joinRoom' });
}
if (isOmnichannelRoom(room) && !room.open) {
throw new MeteorError('room-closed', 'Room is closed', { method: 'joinRoom' });
}
if (!(await Authorization.canAccessRoom(room, user))) {
throw new MeteorError('error-not-allowed', 'Not allowed', { method: 'joinRoom' });
}
if (
FederationActions.shouldPerformFederationAction(room) &&
!isUserNativeFederated(user) &&
!(await FederationMatrix.canUserAccessFederation(user))
) {
throw new MeteorError('error-not-authorized-federation', 'Not authorized to access federation', { method: 'joinRoom' });
}
if (isRoomWithJoinCode(room) && !(await Authorization.hasPermission(user._id, 'join-without-join-code'))) {
if (!joinCode) {
throw new MeteorError('error-code-required', 'Code required', { method: 'joinRoom' });
}View on GitHub (pinned to b2c16d5842)
Solutions
- Re-open the conversation through the omnichannel UI/API (e.g. take/claim the inquiry) instead of calling join
- Filter out closed rooms before attempting a join
- On error, refresh the room state in the client and stop offering join for closed conversations
Defensive patterns
Strategy: validation
Validate before calling
import { isOmnichannelRoom } from '@rocket.chat/core-typings';
if (isOmnichannelRoom(room) && !room.open) {
// closed conversation: reopen via omnichannel flow, never join()
return reopenConversation(room._id);
} Type guard
const isJoinableOmnichannelRoom = (room: IRoom): boolean => !isOmnichannelRoom(room) || room.open === true;
Try / catch
try {
await roomService.join({ room, user });
} catch (err) {
if (err?.error === 'room-closed') {
// refresh state and offer to reopen the conversation; join() will keep failing while closed
return refreshRoomAndPromptReopen(room._id);
}
throw err;
} Prevention
- Filter closed rooms out of joinable lists in the UI
- Handle conversation-closed events so stale room references are dropped
- Reopen closed omnichannel conversations through the omnichannel API, not join()
When it happens
Trigger: Calling join on an omnichannel room whose conversation is already closed — the visitor ended it, an agent closed it, or routing/timeout closed it.
Common situations: Agents trying to rejoin closed conversations directly via the join API; clients holding stale room references after the conversation closed.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/04053118f9116721.
Report an issue: GitHub.