BookStackApp/BookStack · error · PermissionsException
errors.role_system_cannot_be_deleted
Error message
errors.role_system_cannot_be_deleted
What it means
PermissionsRepo::deleteRole refuses to delete a system role (a role whose system_name is in the protected $systemRoles list, e.g. 'admin'). These roles are essential to BookStack's permission model, so deletion throws PermissionsException before any database changes.
Source
Thrown at app/Permissions/PermissionsRepo.php:134
$role->permissions()->sync($permissions);
}
/**
* Delete a role from the system.
* Check it's not an admin role or set as default before deleting.
* If a migration Role ID is specified, the users assigned to the current role
* will be added to the role of the specified id.
*
* @throws PermissionsException
* @throws Exception
*/
public function deleteRole(int $roleId, int $migrateRoleId = 0): void
{
$role = $this->getRoleById($roleId);
// Prevent deleting admin role or default registration role.
if ($role->system_name && in_array($role->system_name, $this->systemRoles)) {
throw new PermissionsException(trans('errors.role_system_cannot_be_deleted'));
} elseif ($role->id === intval(setting('registration-role'))) {
throw new PermissionsException(trans('errors.role_registration_default_cannot_delete'));
}
(new DatabaseTransaction(function () use ($migrateRoleId, $role) {
if ($migrateRoleId !== 0) {
$newRole = Role::query()->find($migrateRoleId);
if ($newRole) {
$users = $role->users()->pluck('id')->toArray();
$newRole->users()->sync($users);
}
}
$role->entityPermissions()->delete();
$role->jointPermissions()->delete();
Activity::add(ActivityType::ROLE_DELETE, $role);
$role->delete();
}))->run();View on GitHub (pinned to 18f8469a1c)
Solutions
- Do not delete the system role; skip roles whose system_name is non-empty/protected in your script
- Reassign its users/permissions to another role first if the intent is to retire the role's assignments (though the role itself must remain)
- If a custom role wrongly has a system_name, fix the roles table data so only built-in roles carry system names
- Wrap deletion in a check: fetch the role and verify it is not a system role before calling deleteRole
Example fix
// before
foreach ($roles as $role) { $repo->deleteRole($role->id); }
// after
foreach ($roles as $role) {
if ($role->system_name) { continue; }
$repo->deleteRole($role->id);
} Defensive patterns
Strategy: validation
Validate before calling
$role = (new PermissionsRepo(app()))->getRoleById($roleId);
if ($role->system_name && in_array($role->system_name, ['admin', 'public'])) {
throw new \InvalidArgumentException("Role {$roleId} is a system role and cannot be deleted");
} Type guard
function isDeletableRole(object $role): bool {
return empty($role->system_name);
} Try / catch
try {
$repo->deleteRole($roleId);
} catch (\BookStack\Exceptions\PermissionsException $e) {
if (str_contains($e->getMessage(), 'role_system_cannot_be_deleted')) {
// skip or alert: protected system role
}
} Prevention
- Filter out roles with a non-empty system_name before bulk deletion
- Never assign system_name values to custom roles
- Document that admin/public roles are immutable
- Test role-cleanup scripts against a staging database first
When it happens
Trigger: Calling deleteRole($roleId) (directly or via the roles UI) where the target role has a system_name matching one of the protected system roles, e.g. attempting to delete the Admin role.
Common situations: Cleanup scripts iterating over all roles and deleting them; admin UI misuse; seeders/tests trying to reset roles to a clean state.
Related errors
- errors.role_registration_default_cannot_delete
- errors.role_cannot_remove_only_admin
- errors.chapter_not_found
- errors.page_not_found
- Book to move chapter into not found
AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02).
Data as JSON: /api/errors/1688a17966453fd4.
Report an issue: GitHub.