{"record":{"id":"1688a17966453fd4","repo":"BookStackApp/BookStack","slug":"errors-role-system-cannot-be-deleted","errorCode":null,"errorMessage":"errors.role_system_cannot_be_deleted","messagePattern":"errors\\.role_system_cannot_be_deleted","errorType":"exception","errorClass":"PermissionsException","httpStatus":null,"severity":"error","filePath":"app/Permissions/PermissionsRepo.php","lineNumber":134,"sourceCode":"        $role->permissions()->sync($permissions);\n    }\n\n    /**\n     * Delete a role from the system.\n     * Check it's not an admin role or set as default before deleting.\n     * If a migration Role ID is specified, the users assigned to the current role\n     * will be added to the role of the specified id.\n     *\n     * @throws PermissionsException\n     * @throws Exception\n     */\n    public function deleteRole(int $roleId, int $migrateRoleId = 0): void\n    {\n        $role = $this->getRoleById($roleId);\n\n        // Prevent deleting admin role or default registration role.\n        if ($role->system_name && in_array($role->system_name, $this->systemRoles)) {\n            throw new PermissionsException(trans('errors.role_system_cannot_be_deleted'));\n        } elseif ($role->id === intval(setting('registration-role'))) {\n            throw new PermissionsException(trans('errors.role_registration_default_cannot_delete'));\n        }\n\n        (new DatabaseTransaction(function () use ($migrateRoleId, $role) {\n            if ($migrateRoleId !== 0) {\n                $newRole = Role::query()->find($migrateRoleId);\n                if ($newRole) {\n                    $users = $role->users()->pluck('id')->toArray();\n                    $newRole->users()->sync($users);\n                }\n            }\n\n            $role->entityPermissions()->delete();\n            $role->jointPermissions()->delete();\n            Activity::add(ActivityType::ROLE_DELETE, $role);\n            $role->delete();\n        }))->run();","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/app/Permissions/PermissionsRepo.php#L116-L152","documentation":"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.","triggerScenarios":"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.","commonSituations":"Cleanup scripts iterating over all roles and deleting them; admin UI misuse; seeders/tests trying to reset roles to a clean state.","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"],"exampleFix":"// before\nforeach ($roles as $role) { $repo->deleteRole($role->id); }\n// after\nforeach ($roles as $role) {\n    if ($role->system_name) { continue; }\n    $repo->deleteRole($role->id);\n}","handlingStrategy":"validation","validationCode":"$role = (new PermissionsRepo(app()))->getRoleById($roleId);\nif ($role->system_name && in_array($role->system_name, ['admin', 'public'])) {\n    throw new \\InvalidArgumentException(\"Role {$roleId} is a system role and cannot be deleted\");\n}","typeGuard":"function isDeletableRole(object $role): bool {\n    return empty($role->system_name);\n}","tryCatchPattern":"try {\n    $repo->deleteRole($roleId);\n} catch (\\BookStack\\Exceptions\\PermissionsException $e) {\n    if (str_contains($e->getMessage(), 'role_system_cannot_be_deleted')) {\n        // skip or alert: protected system role\n    }\n}","preventionTips":["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"],"tags":["permissions","roles","validation","bookstack"],"backgroundTag":"system-role-delete-forbidden","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}