RocketChat/Rocket.Chat · error · Meteor.Error
error-not-allowed
error-not-allowed
Error message
Not Allowed
What it means
Thrown by GET groups.online when the caller cannot access the resolved private group: canAccessRoomAsync(room, this.user) returns false. groups.online lists online members of a group, and for private groups the caller must be a member (or hold access-granting permission/ABAC attributes). Note this endpoint returns 'Group does not exists' as a plain failure for a missing room, but a real room without access raises this error-not-allowed Meteor error.
Solutions
- Add the calling account to the group (groups.invite by an owner) or use an account with room-administration access rights
- Scope monitoring to rooms the service account is a member of; expose group membership lists to it explicitly
- Handle this error distinctly from 'Group does not exists' - it means access denied, not absent
Example fix
// before
const { online } = await botSdk.get('groups.online', { roomId: secretGroupId }); // bot not a member
// after
await ownerSdk.post('groups.invite', { roomId: secretGroupId, username: botUsername });
const { online } = await botSdk.get('groups.online', { roomId: secretGroupId }); Defensive patterns
Strategy: try-catch
Validate before calling
// cheap pre-flight with the same token: only poll groups.online for rooms the token can see
const visible = await sdk.get('rooms.info', { roomId }).catch(() => null);
if (!visible) throw new Error('token cannot access this group'); Try / catch
try {
const { online } = await sdk.get('groups.online', { roomId });
} catch (e) {
if (e.error === 'error-not-allowed') {
// access denied for this private group: stop polling it with this token, alert operator
} else if (e.errorType === 'error' || /does not exists/.test(String(e.message))) {
// distinct branch: room genuinely missing
}
} Prevention
- Give presence/monitoring service accounts explicit membership in every group they watch
- Distinguish this error from the plain 'Group does not exists' failure - different remedies
- Re-check access after kicks/permission changes instead of letting polls fail forever
When it happens
Trigger: GET groups.online?roomId=<private group id> with a token whose user is not in that group; a bot account asked to monitor online users of an exclusive/secret group it was never invited to; access revoked (user kicked) while their client keeps polling presence.
Common situations: Presence dashboards built on service accounts that are not group members; permission model changes (ABAC rollout) silently revoking implicit access; monitoring tooling working for public channels but pointed at private groups.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@e4b8178b20 (2026-09-08).
Data as JSON: /api/errors/54f8d873bd2dfac0.
Report an issue: GitHub.
Appendix: source
Thrown at apps/meteor/server/api/v1/groups.ts:1298
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 hidden = await getUsersHiddenFrom(this.userId);
const online: Pick<IUser, '_id' | 'username'>[] = filterHiddenUsers(
await Users.findUsersNotOffline({
projection: {
username: 1,
},
}).toArray(),
hidden,
);
const onlineInRoom = await Promise.all(
online.map(async (user) => {
const subscription = await Subscriptions.findOneByRoomIdAndUserId(room._id, user._id, {
projection: { _id: 1, username: 1 },
});View on GitHub (pinned to e4b8178b20)