RocketChat/Rocket.Chat · error · Meteor.Error

error-action-not-allowed

error-action-not-allowed

Error message

Leaving the app without an active admin is not allowed

What it means

setUserActiveStatus throws error-action-not-allowed when deactivating (active=false) the last remaining active admin: it checks the target is an admin (findOneAdmin) and that the count of active users in the admin role is exactly 1. This is a deliberate guard to prevent locking the workspace out of administration.

Source

Thrown at apps/meteor/server/lib/users/setUserActiveStatus.ts:78

	const user = await Users.findOneById(userId);

	if (!user) {
		return false;
	}

	if (isUserFederated(user)) {
		throw new Meteor.Error('error-user-is-federated', 'Cannot change federated users status', {
			method: 'setUserActiveStatus',
		});
	}

	// Users without username can't do anything, so there is no need to check for owned rooms
	if (user.username != null && !active) {
		const userAdmin = await Users.findOneAdmin(userId || '');
		const adminsCount = await Users.countActiveUsersInRoles(['admin']);
		if (userAdmin && adminsCount === 1) {
			throw new Meteor.Error('error-action-not-allowed', 'Leaving the app without an active admin is not allowed', {
				method: 'removeUserFromRole',
				action: 'Remove_last_admin',
			});
		}

		const subscribedRooms = await getSubscribedRoomsForUserWithDetails(userId);
		// give omnichannel rooms a special treatment :)
		const chatSubscribedRooms = subscribedRooms.filter(({ t }) => t !== 'l');
		const livechatSubscribedRooms = subscribedRooms.filter(({ t }) => t === 'l');

		if (shouldRemoveOrChangeOwner(chatSubscribedRooms) && !confirmRelinquish) {
			const rooms = await getUserSingleOwnedRooms(chatSubscribedRooms as []);
			throw new Meteor.Error('user-last-owner', '', rooms);
		}

		// We don't want one killing the other :)
		await Promise.allSettled([
			closeOmnichannelConversations(user, livechatSubscribedRooms, executedBy),

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Promote another (active) user to the admin role first, then retry the deactivation.
  2. Or reactivate an existing inactive admin before disabling the current one.
  3. Adjust bulk scripts to skip admins or verify at least one other active admin remains before each deactivation.
Defensive patterns

Strategy: validation

Validate before calling

const user = await Users.findOneAdmin(userId);
if (user) {
  const activeAdmins = await Users.countActiveUsersInRoles(['admin']);
  if (activeAdmins <= 1) {
    throw new Meteor.Error('error-last-admin', 'Promote another active admin before deactivating this user');
  }
}
await setUserActiveStatus(userId, false, confirmRelinquish);

Prevention

When it happens

Trigger: Deactivating the sole active admin account — typically via the admin UI's deactivate action or the REST users.deactivate endpoint; also hit by scripts that deactivate users in bulk including every admin.

Common situations: Offboarding the founding admin without naming a successor; test/demo workspaces with a single admin account; automated deactivation scripts not excluding admins.

Related errors


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