flarum/framework · error · DomainException

Cannot delete the root admin

Error message

Cannot delete the root admin

What it means

The User model's boot() registers a `deleting` hook that throws a DomainException when the user being deleted has id 1, protecting the root admin account from deletion. This is a hard business rule of the forum: exactly one seeded super-admin must always exist. Any deletion attempt (Eloquent delete(), mass delete, etc.) on that row is aborted.

Solutions

  1. Skip user id 1 in deletion loops/cron jobs
  2. Reassign or demote other admins instead of deleting the root account
  3. If the account data must be erased, anonymise its columns rather than deleting the row
  4. For fresh installs where id 1 is a mistake, rebuild the database so the intended admin is the seeded root

Example fix

// before
User::where('last_active', '<', $cutoff)->each->delete();
// after
User::where('last_active', '<', $cutoff)->where('id', '!=', 1)->each->delete();
Defensive patterns

Strategy: try-catch

Validate before calling

if ($user->id === 1) { /* skip deletion or anonymise instead */ }

Try / catch

try { $user->delete(); } catch (DomainException $e) { Log::notice('Root admin deletion blocked'); continue; }

Prevention

When it happens

Trigger: Calling $user->delete() (or User::destroy(1), bulk deletes matching id 1, or cascade paths) on the user with id == 1.

Common situations: Seeders or cleanup scripts that purge inactive users including id 1; admin panel user deletion on the original admin; GDPR/erasure jobs that attempt to hard-delete every account.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of flarum/framework@4b939f6853 (2026-09-15). Data as JSON: /api/errors/b3db3fe4e3a64c86. Report an issue: GitHub.

Appendix: source

Thrown at framework/core/src/User/User.php:162

     *
     * @var callable[]
     */
    protected static array $passwordCheckers;

    /**
     * Difference from the current `last_seen` attribute value before `updateLastSeen()`
     * will update the attribute on the DB. Measured in seconds.
     */
    private const LAST_SEEN_UPDATE_DIFF = 180;

    public static function boot()
    {
        parent::boot();

        // Don't allow the root admin to be deleted.
        static::deleting(function (self $user) {
            if ($user->id == 1) {
                throw new DomainException('Cannot delete the root admin');
            }

            $avatarPath = $user->getRawOriginal('avatar_url');

            if ($avatarPath) {
                resolve(AvatarUploader::class)->deleteAllVariants($avatarPath);
            }
        });

        static::deleted(function (self $user) {
            $user->raise(new Deleted($user));

            Notification::whereSubject($user)->delete();
        });

        static::creating(function (self $user) {
            $user->joined_at = Carbon::now();

View on GitHub (pinned to 4b939f6853)