invoke-ai/InvokeAI · error · HTTPException
Cannot remove the last administrator
Error message
Cannot remove the last administrator
What it means
HTTP 400 raised by update_user when the change would demote or deactivate the last active administrator (auth.py:603). Authorization is derived from the database on every request, so removing the final admin would irreversibly lock everyone out of administration; LAST_ADMIN_DETAIL communicates this.
Source
Thrown at invokeai/app/api/routers/auth.py:603
request.is_active is False or request.is_admin is True or request.password is not None
):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=SYSTEM_USER_PROTECTED_DETAIL,
)
# Demoting or deactivating the last administrator is irreversible: authorization is
# derived from the database on every request, so the caller loses admin access
# immediately and no authenticated path back exists. It would also drop `has_admin()`
# to zero, which re-opens the unauthenticated `/auth/setup` endpoint to any caller.
# `delete_user` guards the same invariant.
if (
before.is_admin
and before.is_active
and (request.is_admin is False or request.is_active is False)
and user_service.count_admins() <= 1
):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=LAST_ADMIN_DETAIL,
)
try:
changes = UserUpdateRequest(
display_name=request.display_name,
password=request.password,
is_admin=request.is_admin,
is_active=request.is_active,
)
updated = user_service.update(user_id, changes, strict_password_checking=config.strict_password_checking)
except ValueError as e:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) from e
# Authorization state changed — notify live connections (open sockets, the
# session processor) so demotion/deactivation takes effect immediately
# instead of persisting until reconnect or token expiry. A password reset bumpsView on GitHub (pinned to 0b6a024f2f)
Solutions
- Promote another user to admin first (PATCH {"is_admin": true}), then demote/deactivate the original admin.
- Reorder bulk operations: grant new admins before removing old ones.
- Skip the last active admin in cleanup scripts.
- If already locked out at the DB level, restore from backup or fix is_admin flags directly in the users table as a last resort.
Example fix
// before: demote before promoting a successor
await api.patch(`/users/${oldAdmin}`, {is_admin:false});
await api.patch(`/users/${newAdmin}`, {is_admin:true}); // 400: last admin
// after: promote successor first
await api.patch(`/users/${newAdmin}`, {is_admin:true});
await api.patch(`/users/${oldAdmin}`, {is_admin:false}); Defensive patterns
Strategy: validation
Validate before calling
async function canRemoveAdmin(id) {
const users = await (await fetch('/api/v1/users')).json();
const target = users.find(u => u.id === id);
const activeAdmins = users.filter(u => u.is_admin && u.is_active);
if (target?.is_admin && target.is_active && activeAdmins.length <= 1) {
return ['cannot demote/deactivate the last active administrator'];
}
return []; // [] => safe
} Try / catch
try {
await api.patch(`/users/${id}`, {is_admin:false});
} catch (e) {
if (e.status === 400 && e.detail.includes('last administrator')) {
throw new Error('Promote another admin before demoting this one');
}
throw e;
} Prevention
- Always promote a successor admin before demoting or deactivating the current one
- Count active admins before running bulk deactivation scripts
- Exclude the last active admin from cleanup operations
- Keep at least two active admins on production installs for safety
When it happens
Trigger: PATCH /api/v1/users/{id} on the sole active admin with either "is_admin": false or "is_active": false, while `user_service.count_admins() <= 1`.
Common situations: Single-admin installs where the owner tries to demote themselves; cleanup scripts deactivating all users including the only admin; transferring admin rights by demoting before the new admin was promoted.
Related errors
- The system user cannot be deleted, deactivated, promoted to
- Administrator account already configured
- str(e)
- str(e) (ValueError from user service update, e.g. LastAdmini
- Current password is required to set a new password
AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29).
Data as JSON: /api/errors/267fa90ceb916aa3.
Report an issue: GitHub.