RocketChat/Rocket.Chat · error · Meteor.Error

error-not-authorized

error-not-authorized

Error message

Not authorized

What it means

Thrown when hasPermissionAsync(userId, 'archive-room', room._id) returns false: the user is valid, the room exists and its type allows archiving, but this user lacks the archive-room permission for that specific room. The permission is evaluated with the room id as scope, so both global role grants and room-scoped overrides apply.

Source

Thrown at apps/meteor/server/meteor-methods/rooms/archiveRoom.ts:38

export const executeArchiveRoom = async (userId: string, rid: string) => {
	check(rid, String);

	const user = await Users.findOneById(userId, { projection: { username: 1, name: 1 } });
	if (!user || !isRegisterUser(user)) {
		throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'archiveRoom' });
	}

	const room = await Rooms.findOneById(rid);
	if (!room) {
		throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'archiveRoom' });
	}

	if (!(await roomCoordinator.getRoomDirectives(room.t).allowMemberAction(room, RoomMemberActions.ARCHIVE, userId))) {
		throw new Meteor.Error('error-direct-message-room', `rooms type: ${room.t} can not be archived`, { method: 'archiveRoom' });
	}

	if (!(await hasPermissionAsync(userId, 'archive-room', room._id))) {
		throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'archiveRoom' });
	}

	return archiveRoom(rid, user);
};

Meteor.methods<ServerMethods>({
	async archiveRoom(rid) {
		methodDeprecationLogger.method('archiveRoom', '9.0.0', '/v1/channels.archive');
		const userId = Meteor.userId();
		if (!userId) {
			throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'archiveRoom' });
		}

		return executeArchiveRoom(userId, rid);
	},
});

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Grant archive-room to the acting user's role (Administration > Permissions), globally or scoped to the room.
  2. Have a user who holds the permission (room owner or admin) perform the archive.
  3. Gate the archive action client-side on the same permission so the method is never called without it.

Example fix

// before
Meteor.call('archiveRoom', rid);

// after
if (hasPermission(Meteor.userId(), 'archive-room', rid)) {
  Meteor.call('archiveRoom', rid);
} else {
  showToast('Not authorized to archive this room');
}
Defensive patterns

Strategy: try-catch

Validate before calling

// with the roles subscription active, mirror the server check:
if (!hasPermission(Meteor.userId(), 'archive-room', rid)) {
  // hide/disable the archive action instead of calling the method
}

Try / catch

try {
  await Meteor.callAsync('archiveRoom', rid);
} catch (e) {
  if (e instanceof Meteor.Error && e.error === 'error-not-authorized') {
    showToast('You are not allowed to archive this room');
    return; // do not retry - a permission will not appear by retrying
  }
  throw e;
}

Prevention

When it happens

Trigger: A plain member (not owner/moderator) calls archiveRoom; the archive-room permission was revoked from the user's role after the UI rendered; room-scoped permission settings deny the caller.

Common situations: Workspace policies limiting archive to owners/moderators; permission changes not propagated to the client UI; custom roles created without archive-room.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/00e7a3527ddff01e. Report an issue: GitHub.