RocketChat/Rocket.Chat · error · Meteor.Error
error-not-allowed
error-not-allowed
Error message
Not allowed
What it means
Thrown by the readMessages Meteor method when canAccessRoomAsync(room, user) returns false. The server already resolved the room and the user, so the failure is purely authorization: the caller is not allowed to view this room (not a member of the private room/channel/team, or lacking the required view permission). Marking a room as read is only accepted for rooms the user can legally access.
Source
Thrown at apps/meteor/server/meteor-methods/messages/readMessages.ts:34
Meteor.methods<ServerMethods>({
async readMessages(rid, readThreads = false) {
check(rid, String);
const userId = Meteor.userId();
if (!userId) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
method: 'readMessages',
});
}
const user = ((await Meteor.userAsync()) as IUser | null) ?? undefined;
const room = await Rooms.findOneById(rid);
if (!room) {
throw new Meteor.Error('error-room-does-not-exist', 'This room does not exist', { method: 'readMessages' });
}
if (!(await canAccessRoomAsync(room, user))) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'readMessages' });
}
await readMessages(room, userId, readThreads);
},
});
View on GitHub (pinned to b2c16d5842)
Solutions
- Confirm the user actually has an active subscription/membership for this rid before calling readMessages
- Check the user's role permissions in Admin -> Permissions (view-c-room, view-p-room, view-joined-room) and grant the missing one
- Verify the rid is the room the UI currently shows; a deleted room raises error-room-does-not-exist instead, so 'Not allowed' means wrong membership, not wrong id
- Re-login if the DDP connection carries a stale user context after permissions changed
Example fix
// before: blind call with a possibly stale rid
Meteor.call('readMessages', rid);
// after: only mark rooms the user is currently subscribed to
const subscription = subscriptions.find((s) => s.rid === rid);
if (subscription) {
await Meteor.callAsync('readMessages', rid);
} Defensive patterns
Strategy: validation
Validate before calling
// client: only mark rooms the user is currently subscribed to
const canMarkRead = (rid: string): boolean =>
Boolean(Meteor.userId()) && subscriptions.some((s) => s.rid === rid);
if (canMarkRead(rid)) {
await Meteor.callAsync('readMessages', rid);
} Try / catch
try {
await Meteor.callAsync('readMessages', rid);
} catch (e: any) {
if (e?.error === 'error-not-allowed') {
// membership revoked: refresh subscriptions and clear the local unread badge
return;
}
throw e;
} Prevention
- Drive unread-marking from the user's live subscription collection, not from cached room lists
- Re-check membership after reconnect or workspace switch before touching read state
- Log rid + userId when this fires to spot permission regressions early
When it happens
Trigger: Meteor.call('readMessages', rid) where rid is: a private channel or team the user was never added to or was removed from; a direct message the user is not a participant in; a room whose type requires a permission (view-c-room, view-p-room, view-joined-room) the user's role does not hold.
Common situations: Client marks unread badges for rooms from a stale cached list after the user was kicked or left; a custom integration iterates every room id it ever saw and marks them read; an admin revokes view-joined-room from a role; room was deleted and recreated so the old rid now points at a room the user cannot see.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/387e2ea41b36125c.
Report an issue: GitHub.