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

  1. Use the membership path intended for that room type (e.g. omnichannel rooms are joined by accepting an inquiry, direct rooms by message exchange)
  2. Check the room type's directives (allowMemberAction for JOIN) before offering a join action to the user
  3. 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

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.