RocketChat/Rocket.Chat · error · MeteorError
error-not-allowed
error-not-allowed
Error message
Not allowed
What it means
RoomService.join() first asks the room-type coordinator whether the member action JOIN is allowed: roomCoordinator.getRoomDirectives(room.t)?.allowMemberAction(room, RoomMemberActions.JOIN, user._id). If the directive for that room type refuses the join for this user, join throws Meteor error-not-allowed ('Not allowed', method 'joinRoom').
Source
Thrown at apps/meteor/server/services/room/service.ts:186
async saveRoomTopic(
roomId: string,
roomTopic: string | undefined,
user: Pick<IUser, 'username' | '_id' | 'federation' | 'federated'>,
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' });
}
View on GitHub (pinned to b2c16d5842)
Solutions
- Use the membership path intended for that room type (e.g. omnichannel rooms are joined by accepting an inquiry, direct rooms by message exchange)
- Check the room type's directives (allowMemberAction for JOIN) before offering a join action to the user
- Verify with the room-type configuration/permissions that JOIN is meant to be allowed for this role
Defensive patterns
Strategy: validation
Validate before calling
import { roomCoordinator } from '/app/lib/rooms/roomCoordinator';
const allowed = await roomCoordinator.getRoomDirectives(room.t)?.allowMemberAction(room, RoomMemberActions.JOIN, uid);
if (!allowed) {
// use the room-type-appropriate membership flow instead of join()
return useTypeSpecificFlow(room);
} Type guard
const canJoinRoomType = async (room: IRoom, uid: string): Promise<boolean> => Boolean(await roomCoordinator.getRoomDirectives(room.t)?.allowMemberAction(room, RoomMemberActions.JOIN, uid));
Try / catch
try {
await roomService.join({ room, user });
} catch (err) {
if (err?.error === 'error-not-allowed' && err?.details?.method === 'joinRoom') {
// room type forbids self-join: use invitation/type-specific flow; retrying join() cannot succeed
return notifyUser('This room cannot be joined directly');
}
throw err;
} Prevention
- Offer join actions only for room types whose directives allow JOIN
- Use type-specific membership flows (inquiries for omnichannel, invitations for private rooms)
- Check allowMemberAction before calling join()
When it happens
Trigger: Calling join on a room whose type directives disallow user-initiated JOIN — e.g. direct rooms and some omnichannel/custom room types where membership is managed implicitly rather than via join.
Common situations: Clients hitting the generic join API for room types that do not support it; custom/EE room types with restrictive member actions; joins that should go through a type-specific flow.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/3ebe17b4338f7346.
Report an issue: GitHub.