RocketChat/Rocket.Chat · error · Meteor.Error

error-not-allowed

error-not-allowed

Error message

Not Allowed

What it means

Thrown by groups.online (groups.ts:1293-1295). The room was found by query/_id and is a private group (t:'p'), but canAccessRoomAsync(room, this.user) returned false: the caller is not a member and lacks an override permission that grants visibility into the group's online roster. This is the authorization gate; it surfaces as a generic 'Not Allowed' (403-shaped) so as not to confirm the room's existence.

Source

Thrown at apps/meteor/server/api/v1/groups.ts:1294

		const { _id } = this.queryParams;

		if ((!query || Object.keys(query).length === 0) && !_id) {
			return API.v1.failure('Invalid query');
		}

		const filter = {
			...query,
			...(_id ? { _id } : {}),
			t: 'p',
		};

		const room = await Rooms.findOne(filter as Record<string, any>);
		if (!room) {
			return API.v1.failure('Group does not exists');
		}

		if (!(await canAccessRoomAsync(room, this.user))) {
			throw new Meteor.Error('error-not-allowed', 'Not Allowed');
		}

		const online: Pick<IUser, '_id' | 'username'>[] = await Users.findUsersNotOffline({
			projection: {
				username: 1,
			},
		}).toArray();

		const onlineInRoom = await Promise.all(
			online.map(async (user) => {
				const subscription = await Subscriptions.findOneByRoomIdAndUserId(room._id, user._id, {
					projection: { _id: 1, username: 1 },
				});
				if (subscription) {
					return {
						_id: user._id,
						username: user.username,
					};

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Ensure the caller is a member of the group, or use a token whose user has the appropriate override permission (e.g. view-room-administration) that canAccessRoomAsync accepts.
  2. Have a member/admin invite the calling user (groups.invite) before requesting online status.
  3. If presence is needed broadly, prefer a presence/subscription endpoint the user is authorized for rather than groups.online on private rooms.
  4. Confirm the authenticated token corresponds to the user you expect (stale tokens of ex-members fail here).

Example fix

// before
await GET('/api/v1/groups.online', { query: { _id: groupId } }); // caller not a member -> 403 error-not-allowed

// after
const me = await GET('/api/v1/me');
if (!await isMember(groupId, me.userId)) {
  // have a member invite `me.userId` first, or use an admin token with view-room-administration
  await POST('/api/v1/groups.invite', { roomId: groupId, userId: me.userId });
}
await GET('/api/v1/groups.online', { query: { _id: groupId } });
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight: confirm caller access to the private group before requesting online roster.
const info = await api.get('/api/v1/groups.info', { roomId }).catch(() => null);
if (!info) throw new Error('No access or no such group');
await api.get('/api/v1/groups.online', { query: { _id: roomId } });

Type guard

function isPrivateGroup(r) {
  return r != null && r.t === 'p';
}

Try / catch

try {
  await api.get('/api/v1/groups.online', { query: { _id: roomId } });
} catch (e) {
  if (isMeteorError(e) && e.reason === 'error-not-allowed') {
    // caller lacks access; invite the caller or use an authorized service token
  }
  throw e;
}

Prevention

When it happens

Trigger: GET groups.online with a valid query/_id for a private group while the authenticated user is neither a member nor a moderator/admin with an override (e.g. 'view-room-administration' / 'view-all-rooms').

Common situations: A dashboard/bot tries to report online members for groups it does not belong to. A user navigates to a private group's online list without having joined. Tests querying as an uninvited user.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12). Data as JSON: /api/errors/54f8d873bd2dfac0. Report an issue: GitHub.