#2073·Marzban

DELETE /api/user/{username} crashes with 500 when the deleting admin has no DB-backed Admin row, even though the deletion succeeds

Author: tfisth1isCreated Jun 20, 2026Updated Jun 20, 2026

Describe the bug Calling DELETE /api/user/{username} correctly removes the user from the database, but the request still returns HTTP 500 to the caller -- making it look like the deletion failed when it actually succeeded.

Root cause In app/routers/user.py, the remove_user endpoint:

python
crud.remove_user(db, dbuser)
bg.add_task(xray.operations.remove_user, dbuser=dbuser)
bg.add_task(
    report.user_deleted, username=dbuser.username, user_admin=Admin.model_validate(dbuser.admin), by=admin
)

Admin.model_validate(dbuser.admin) is evaluated synchronously, while building the arguments for bg.add_task(...) -- it's not deferred into the background task. If dbuser.admin is None, this raises a Pydantic ValidationError inside the request, after the deletion has already committed.

How to reproduce dbuser.admin is None when the user was created by the env-var-based sudo admin (SUDO_USERNAME/SUDO_PASSWORD in .env), which has no corresponding row in the admins table (confirm via GET /api/admins returning []).

  1. Set SUDO_USERNAME/SUDO_PASSWORD in .env, with zero rows in the admins table.
  2. Authenticate as that sudo admin, create a user.
  3. DELETE /api/user/{username} -> HTTP 500, pydantic_core._pydantic_core.ValidationError: 1 validation error for Admin ... input_value=None.
  4. GET /api/user/{username} -> 404. The user is actually gone.

Expected behavior Either skip the report task when dbuser.admin is None, or guard the Admin.model_validate() call, and return a clean 200/204 since the deletion itself succeeded.

Environment

  • Marzban version: 0.8.4 (latest release, also reproduced on current master)
  • Confirmed via Docker logs and direct API testing