invoke-ai/InvokeAI · error · LastAdministratorError
LAST_ADMIN_DETAIL
Error message
LAST_ADMIN_DETAIL
What it means
_assert_not_last_admin raises LastAdministratorError(LAST_ADMIN_DETAIL) ('Cannot remove the last administrator') when an update or delete would demote, deactivate, or remove the only remaining active admin. The guard counts active admins inside the same write-locked transaction as the write, so it is race-safe. The system user is also protected separately via _assert_system_user_protected.
Source
Thrown at invokeai/app/services/users/users_default.py:409
being ``None``, which would also describe a rename. Only a change that actually
revokes administrator status is checked, so renaming the last admin stays allowed.
"""
cursor.execute("SELECT is_admin, is_active FROM users WHERE user_id = ?", (user_id,))
row = cursor.fetchone()
if row is None:
return
# An admin who is already inactive is not counted, so removing them changes nothing.
if not (bool(row[0]) and bool(row[1])):
return
if not (is_deleting or is_admin is False or is_active is False):
return
cursor.execute("SELECT COUNT(*) FROM users WHERE is_admin = TRUE AND is_active = TRUE")
count_row = cursor.fetchone()
if (int(count_row[0]) if count_row else 0) <= 1:
raise LastAdministratorError(LAST_ADMIN_DETAIL)
def _assert_system_user_protected(
self,
user_id: str,
*,
is_deleting: bool = False,
is_admin: bool | None = None,
is_active: bool | None = None,
password: str | None = None,
) -> None:
"""Reject changes to the ``system`` account that no legitimate operation needs.
The system row owns every board, image, workflow, and queue item carried over from
before multiuser support. Deleting or deactivating it strands all of that: queued
items are rejected at dequeue and media reads and saves raise ``PermissionError``.
Promotion and password-setting are refused for a different reason. The system row
is active but has an empty password hash, so it can never authenticate — yetView on GitHub (pinned to 0b6a024f2f)
Solutions
- Promote a second active admin first (update another user with is_admin=True), then demote/delete the original.
- Modify a different field instead: renaming the last admin or changing their password is explicitly allowed; only is_admin=False, is_active=False, or delete trigger the guard.
- Catch LastAdministratorError at the call site and surface the policy message to the end user rather than treating it as a bug.
- For self-service flows, disable the 'demote/deactivate/delete' affordance in the UI when COUNT(active admins) <= 1.
Example fix
// before users.update(last_admin_id, UserUpdateRequest(is_admin=False)) # LastAdministratorError // after users.update(other_user_id, UserUpdateRequest(is_admin=True)) # ensure a successor users.update(last_admin_id, UserUpdateRequest(is_admin=False))
Defensive patterns
Strategy: try-catch
Validate before calling
row = db.run_sql('SELECT COUNT(*) FROM users WHERE is_admin = TRUE AND is_active = TRUE').fetchone()
active_admins = int(row[0]) if row else 0
if active_admins <= 1 and (changes.is_admin is False or changes.is_active is False):
raise PolicyError('Promote another admin before demoting the last one') Try / catch
from invokeai.app.services.users.users_common import LastAdministratorError
try:
users.update(user_id, changes)
except LastAdministratorError:
flash('Cannot remove the last administrator; promote another admin first')
except ValueError as e:
raise Prevention
- Count active admins (is_admin AND is_active) before offering demote/deactivate/delete actions
- Promote a successor admin before removing the current one in teardown/migration scripts
- Hide or disable destructive actions on the last admin in admin UIs
- Remember renames and password changes are safe; only is_admin/is_active changes and deletes are guarded
- Catch LastAdministratorError distinctly from ValueError so policy rejections are not logged as bugs
When it happens
Trigger: update(user_id, UserUpdateRequest(is_admin=False)) or (is_active=False) on the sole active admin; delete(user_id) on the sole active admin; demoting/deactivating the last of two admins after the other was already removed.
Common situations: Admin UIs letting the sole admin demote themselves; cleanup scripts bulk-deactivating users without checking admin counts; fixture teardown deleting the seeded admin before other tests need an admin; concurrent operations that legitimately emptied the admin pool just before your call.
Related errors
- str(e) (ValueError from user service delete, e.g. LastAdmini
- User {user_id} not found
- Default workflows cannot be created via this method
- Default workflows cannot be updated
- Default workflows cannot be deleted
AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29).
Data as JSON: /api/errors/9d06930acf633916.
Report an issue: GitHub.