RocketChat/Rocket.Chat · error · Meteor.Error

error-admin-required

error-admin-required

Error message

You need to have at least one admin

What it means

Thrown by POST roles.removeUserFromRole when role._id === 'admin', adminCount === 1, and the operation would remove the last admin. This is a safety guard preventing lockout. Returns a structured Meteor.Error('error-admin-required', 'You need to have at least one admin').

Source

Thrown at apps/meteor/server/api/v1/roles.ts:303

			if (!user) {
				throw new Meteor.Error('error-invalid-user', 'There is no user with this username');
			}

			const role = await Roles.findOneById(roleId);

			if (!role) {
				throw new Meteor.Error('error-invalid-roleId', 'This role does not exist');
			}

			if (!(await hasAnyRoleAsync(user._id, [role._id], scope))) {
				throw new Meteor.Error('error-user-not-in-role', 'User is not in this role');
			}

			if (role._id === 'admin') {
				const adminCount = await Roles.countUsersInRole('admin');
				if (adminCount === 1) {
					throw new Meteor.Error('error-admin-required', 'You need to have at least one admin');
				}
			}

			await removeUserFromRolesAsync(user._id, [role._id], scope);

			if (settings.get('UI_DisplayRoles')) {
				void api.broadcast('user.roleUpdate', {
					type: 'removed',
					_id: role._id,
					u: {
						_id: user._id,
						username: user.username,
					},
					scope,
				});
			}

			return API.v1.success({

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Promote at least one other user to admin first via roles.addUserToRole, then retry the removal.
  2. If the intent is to change the admin account, grant admin to the new user before revoking the old one.
  3. In automation, check adminCount via roles.getUsersInRole?role=admin and guard against count <= 1.

Example fix

// before
await fetch('/api/v1/roles.removeUserFromRole', { method:'POST', body: JSON.stringify({ roleId: 'admin', username }) });

// after - ensure a second admin exists first
const { total } = await fetch('/api/v1/roles.getUsersInRole?role=admin').then(r=>r.json());
if (total <= 1) {
  await fetch('/api/v1/roles.addUserToRole', { method:'POST', body: JSON.stringify({ roleId:'admin', username: successor }) });
}
await fetch('/api/v1/roles.removeUserFromRole', { method:'POST', body: JSON.stringify({ roleId:'admin', username }) });
Defensive patterns

Strategy: validation

Validate before calling

// Never remove the last admin: ensure >= 2 admins first
const { total } = await fetch('/api/v1/roles.getUsersInRole?role=admin').then(r => r.json());
if (total <= 1) {
  await fetch('/api/v1/roles.addUserToRole', { method:'POST', body: JSON.stringify({ roleId:'admin', username: successor }) });
}
await fetch('/api/v1/roles.removeUserFromRole', { method:'POST', body: JSON.stringify({ roleId:'admin', username }) });

Try / catch

try {
  await fetch('/api/v1/roles.removeUserFromRole', {method:'POST',body:JSON.stringify({roleId:'admin',username})}).then(r=>r.json());
} catch (e) {
  if (e.error === 'error-admin-required') { /* promote another admin, then retry */ }
}

Prevention

When it happens

Trigger: POST /api/v1/roles.removeUserFromRole with roleId 'admin' (or the admin role _id) for the only remaining administrator, globally.

Common situations: Demoting/removing the sole admin account; cleanup script strips all admin grants; removing a user who is the last admin before promoting another.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12). Data as JSON: /api/errors/02c3e36ec2a4b11b. Report an issue: GitHub.