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

  1. Confirm the user actually has an active subscription/membership for this rid before calling readMessages
  2. Check the user's role permissions in Admin -> Permissions (view-c-room, view-p-room, view-joined-room) and grant the missing one
  3. 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
  4. 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

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.