{"record":{"id":"73daa8926c074e6e","repo":"BookStackApp/BookStack","slug":"errors-users-cannot-delete-only-admin","errorCode":null,"errorMessage":"errors.users_cannot_delete_only_admin","messagePattern":"errors\\.users_cannot_delete_only_admin","errorType":"exception","errorClass":"NotifyException","httpStatus":null,"severity":"error","filePath":"app/Users/UserRepo.php","lineNumber":253,"sourceCode":"            'sessions' => ['user_id'],\n        ];\n\n        foreach ($toNullify as $table => $columns) {\n            foreach ($columns as $column) {\n                DB::table($table)\n                    ->where($column, '=', $user->id)\n                    ->update([$column => null]);\n            }\n        }\n    }\n\n    /**\n     * @throws NotifyException\n     */\n    protected function ensureDeletable(User $user): void\n    {\n        if ($this->isOnlyAdmin($user)) {\n            throw new NotifyException(trans('errors.users_cannot_delete_only_admin'), $user->getEditUrl());\n        }\n\n        if ($user->system_name === 'public') {\n            throw new NotifyException(trans('errors.users_cannot_delete_guest'), $user->getEditUrl());\n        }\n    }\n\n    /**\n     * Migrate ownership of items in the system from one user to another.\n     */\n    protected function migrateOwnership(User $fromUser, User|null $toUser): void\n    {\n        $newOwnerValue = $toUser ? $toUser->id : null;\n        DB::table('entities')\n            ->where('owned_by', '=', $fromUser->id)\n            ->update(['owned_by' => $newOwnerValue]);\n    }\n","sourceCodeStart":235,"sourceCodeEnd":271,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/app/Users/UserRepo.php#L235-L271","documentation":"UserRepo::ensureDeletable() throws NotifyException (a user-facing redirect-with-message exception) when an admin attempts to delete the only remaining admin account. Deleting the last admin would leave the instance with no user able to administer it, so BookStack refuses the operation and redirects the user back to the edit page with errors.users_cannot_delete_only_admin.","triggerScenarios":"Users/UserController destroy flow calling UserRepo::destroy on a user for whom isOnlyAdmin() returns true — i.e. the target user has admin role and is the sole member of that role.","commonSituations":"Single-admin BookStack instances where the owner tries to delete their own account via the UI or API without first promoting another user to admin.","solutions":["Promote another user to the Admin role first, then delete the original admin","Instead of deleting, disable or archive the user while keeping admin coverage","Transfer ownership of content and use 'manage users' to assign a new admin","Catch NotifyException in custom code to render a friendly message and redirect"],"exampleFix":"// before\n$this->userRepo->destroy($lastAdminUser);\n// after\nif (!$this->userRepo->isOnlyAdmin($lastAdminUser)) {\n    $this->userRepo->destroy($lastAdminUser);\n}","handlingStrategy":"validation","validationCode":"// Check before deleting\nif ($userRepo->isOnlyAdmin($user)) {\n    return back()->withErrors('Cannot delete the only admin.');\n}\n$userRepo->destroy($user);","typeGuard":"function isSafeToDelete(\\BookStack\\Users\\User $u, \\BookStack\\Users\\UserRepo $repo): bool {\n    return !$repo->isOnlyAdmin($u) && $u->system_name !== 'public';\n}","tryCatchPattern":"try {\n    $userRepo->destroy($user);\n} catch (\\BookStack\\Exceptions\\NotifyException $e) {\n    return redirect($e->redirectUrlTo ?? url('/'))->with('error', $e->getMessage());\n}","preventionTips":["Always keep at least two admin accounts","Never script deletes over all users without filtering","Treat NotifyException as expected UX (redirect+message), not a bug","Check isOnlyAdmin() before bulk operations"],"tags":["php","permissions","users","bookstack"],"backgroundTag":"cannot-delete-last-admin","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}