BookStackApp/BookStack · error · UserUpdateException
errors.role_cannot_remove_only_admin
Error message
errors.role_cannot_remove_only_admin
What it means
UserRepo::setUserRoles() throws UserUpdateException when a role update would demote the last admin: demotingLastAdmin() detects that the target user currently holds the admin role, is the only member of it, and the new role array no longer contains it. This prevents locking the instance out of administration; the exception redirects back to the user's edit URL with errors.role_cannot_remove_only_admin.
Source
Thrown at app/Users/UserRepo.php:312
$adminRole = Role::getSystemRole('admin');
if ($adminRole->users()->count() > 1) {
return false;
}
return true;
}
/**
* Set the assigned user roles via an array of role IDs.
*
* @throws UserUpdateException
*/
protected function setUserRoles(User $user, array $roles): void
{
$roles = array_filter(array_values($roles));
if ($this->demotingLastAdmin($user, $roles)) {
throw new UserUpdateException(trans('errors.role_cannot_remove_only_admin'), $user->getEditUrl());
}
$user->roles()->sync($roles);
}
/**
* Check if the given user is the last admin and their new roles no longer
* contain the admin role.
*/
protected function demotingLastAdmin(User $user, array $newRoles): bool
{
if ($this->isOnlyAdmin($user)) {
$adminRole = Role::getSystemRole('admin');
if (!in_array(strval($adminRole->id), $newRoles)) {
return true;
}
}
View on GitHub (pinned to 18f8469a1c)
Solutions
- Promote a second user to Admin before demoting the last one
- Include the admin role id in the roles array for the sole admin, or skip that user in bulk updates
- Catch UserUpdateException and redirect with a friendly message instead of a raw error
- Check demotingLastAdmin/isOnlyAdmin logic before calling setUserRoles
Example fix
// before
$userRepo->setUserRoles($lastAdmin, [$editorRoleId]);
// after
if (!$userRepo->isOnlyAdmin($lastAdmin)) {
$userRepo->setUserRoles($lastAdmin, [$editorRoleId]);
} Defensive patterns
Strategy: validation
Validate before calling
// Verify the new role set keeps admin coverage
$adminRoleId = \BookStack\Access\Role::getSystemRole('admin')->id;
$willLoseAdmin = $user->hasRole($adminRoleId)
&& !in_array($adminRoleId, $newRoleIds)
&& \BookStack\Access\Role::getSystemRole('admin')->users()->count() === 1;
if ($willLoseAdmin) { return back()->withErrors('Cannot remove the only admin.'); }
$userRepo->setUserRoles($user, $newRoleIds); Try / catch
try {
$userRepo->setUserRoles($user, $roles);
} catch (\BookStack\Exceptions\UserUpdateException $e) {
return redirect($user->getEditUrl())->with('error', $e->getMessage());
} Prevention
- Promote a second admin before demoting the first
- Never replace role arrays wholesale for the sole admin
- Count admin-role members in bulk sync logic
- Catch UserUpdateException in custom controllers
When it happens
Trigger: updateWithoutActivity/createWithoutActivity called with a roles array that omits the admin role for the sole admin user; UI role checkboxes unchecked for the last admin.
Common situations: Admin removing their own admin role; bulk role sync via API that replaces role sets wholesale; scripts importing users with role lists that drop the admin grant.
Related errors
- errors.role_system_cannot_be_deleted
- errors.role_registration_default_cannot_delete
- errors.users_cannot_delete_only_admin
- errors.users_cannot_delete_guest
- errors.chapter_not_found
AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02).
Data as JSON: /api/errors/825878e1d2bc4852.
Report an issue: GitHub.