RocketChat/Rocket.Chat · error · Meteor.Error

error-action-not-allowed

error-action-not-allowed

Error message

Leaving the app without admins is not allowed

What it means

`executeDeleteUser` counts users with the `admin` role (`Users.countDocuments({ roles: 'admin' })`) and refuses the deletion when the count is exactly 1 and the target user is that admin. This is the last-admin protection: without it a workspace could be left with no administrator able to manage it. The thrown details include `action: 'Remove_last_admin'`.

Source

Thrown at apps/meteor/server/meteor-methods/users/deleteUser.ts:37

	const user = await Users.findOneById(userId);
	if (!user) {
		throw new Meteor.Error('error-invalid-user', 'Invalid user to delete', {
			method: 'deleteUser',
		});
	}

	if (user.type === 'app') {
		throw new Meteor.Error('error-cannot-delete-app-user', 'Deleting app user is not allowed', {
			method: 'deleteUser',
		});
	}

	const adminCount = await Users.countDocuments({ roles: 'admin' });

	const userIsAdmin = user.roles?.indexOf('admin') > -1;

	if (adminCount === 1 && userIsAdmin) {
		throw new Meteor.Error('error-action-not-allowed', 'Leaving the app without admins is not allowed', {
			method: 'deleteUser',
			action: 'Remove_last_admin',
		});
	}

	await deleteUser(userId, confirmRelinquish, fromUserId);

	return true;
};

Meteor.methods<ServerMethods>({
	async deleteUser(userId, confirmRelinquish = false) {
		methodDeprecationLogger.method('deleteUser', '9.0.0', '/v1/users.delete');
		check(userId, String);

		const uid = Meteor.userId();
		if (!uid) {
			throw new Meteor.Error('error-not-allowed', 'Not allowed', {

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Promote another user to admin first (Admin → Users → Make Admin), then delete the original.
  2. Before deleting any admin, check the admin count and skip/abort when it is 1.
  3. In automation, order deletions so at least one admin always remains, or create a break-glass admin beforehand.
  4. If you are locked out conceptually, use the workspace owner/registration account or CLI to grant a new admin.

Example fix

// before - deletes even when target is the last admin
await deleteUserFn(uid, targetUserId);

// after - guard the last-admin case client/server side
const admins = users.filter((u) => u.roles?.includes('admin'));
if (admins.length === 1 && admins[0]._id === targetUserId) {
  throw new Error('Promote another admin before deleting the last one');
}
await deleteUserFn(uid, targetUserId);
Defensive patterns

Strategy: validation

Validate before calling

// abort before deleting the last remaining admin
const adminCount = await Users.countDocuments({ roles: 'admin' });
const targetIsAdmin = (await Users.findOneById(userId))?.roles?.includes('admin');
if (adminCount === 1 && targetIsAdmin) {
  throw new Error('Promote another admin before deleting the last one');
}
await deleteUserFn(uid, userId);

Type guard

const wouldLeaveNoAdmin = (adminIds: string[], targetUserId: string): boolean => adminIds.length === 1 && adminIds[0] === targetUserId;

Try / catch

try {
  await Meteor.callAsync('deleteUser', userId);
} catch (e: any) {
  if (e?.error === 'error-action-not-allowed' && e?.details?.action === 'Remove_last_admin') {
    // promote a second admin first, then retry the deletion
  }
}

Prevention

When it happens

Trigger: Calling `deleteUser` (or REST `users.delete`) on the only remaining admin — e.g. cleaning up old admin accounts without first transferring rights, or deleting the seed admin after setup while no second admin exists.

Common situations: Offboarding scripts removing the original admin; downsizing from several admins to one and deleting in the wrong order; fresh installs experimenting with the initial admin account; self-managed servers recovered with a single admin left.

Related errors


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