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
- Promote another user to admin first (Admin → Users → Make Admin), then delete the original.
- Before deleting any admin, check the admin count and skip/abort when it is 1.
- In automation, order deletions so at least one admin always remains, or create a break-glass admin beforehand.
- 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
- Always keep at least two admins (or a break-glass admin) before offboarding any admin.
- Order bulk admin deletions so the last admin is never the deletion target.
- Check the details.action === 'Remove_last_admin' discriminator when handling this error.
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
- error-invalid-user
- error-cannot-delete-app-user
- error-not-allowed
- error-not-allowed
- error-action-not-allowed
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/17fdc55fac53766f.
Report an issue: GitHub.