invoke-ai/InvokeAI · error · HTTPException
str(e) (ValueError from user service update, e.g. LastAdmini
Error message
str(e) (ValueError from user service update, e.g. LastAdministratorError)
What it means
HTTP 400 raised by update_user when `user_service.update(...)` throws ValueError (auth.py:617), detail = `str(e)`. The route pre-checks unknown ids, system-user edits, and last-admin changes, but the service is authoritative and can still reject — e.g. a race where another request already demoted the last admin (LastAdministratorError), or a weak password under strict checking.
Source
Thrown at invokeai/app/api/routers/auth.py:617
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 bumps
# the epoch without touching is_admin/is_active, and must drop the target's open
# sockets too, so it is part of this condition.
if (
before.is_admin != updated.is_admin
or before.is_active != updated.is_active
or before.token_epoch != updated.token_epoch
):
ApiDependencies.invoker.services.events.emit_user_access_changed(
user_id=updated.user_id,
is_admin=updated.is_admin,
is_active=updated.is_active,
token_epoch=updated.token_epoch,
)
View on GitHub (pinned to 0b6a024f2f)
Solutions
- Read the 400 `detail` — it is the service ValueError text (e.g. LastAdministratorError message).
- For last-admin races: promote a new admin first, then retry the demotion/deactivation.
- Use a password that meets strength rules or relax strict_password_checking for dev.
- Serialize admin user-management operations or retry idempotent updates after re-reading state.
- Re-GET the user to see current is_admin/is_active before re-patching.
Example fix
// before: single blind update
await api.patch(`/users/${id}`, {is_active:false});
// after: re-check state and retry once on 400
let r = await api.patch(`/users/${id}`, {is_active:false});
if (r.status === 400) {
const u = await (await api.get(`/users/${id}`)).json();
if (u.is_active) throw new Error(await r.text());
r = await api.patch(`/users/${id}`, {is_active:false});
} Defensive patterns
Strategy: try-catch
Validate before calling
// Re-read current state immediately before updating to shrink the race window
const u = await (await fetch(`/api/v1/users/${id}`)).json();
if (u.is_admin && u.is_active) {
const users = await (await fetch('/api/v1/users')).json();
if (users.filter(x => x.is_admin && x.is_active).length <= 1) {
throw new Error('Promote another admin first');
}
} Try / catch
try {
return await api.patch(`/users/${id}`, changes);
} catch (e) {
if (e.status === 400) {
// detail is the service ValueError, e.g. LastAdministratorError text
const fresh = await fetchUser(id);
if (fresh) return api.patch(`/users/${id}`, changes); // one retry after re-check
throw new Error(`Update rejected: ${e.detail}`);
}
throw e;
} Prevention
- Serialize admin user-management operations across sessions/scripts
- Re-GET the user before retrying a failed update
- Promote new admins before demoting old ones to avoid last-admin races
- Match strict_password_checking policy when sending password changes
- Log the 400 detail verbatim — it names the exact service rejection
When it happens
Trigger: PATCH /users/{id} losing the last-admin race (another concurrent request demoted/deactivated the remaining admin between the pre-check and the update); password change failing strength validation with strict_password_checking; other service-level constraint violations.
Common situations: Two admins editing users simultaneously; automated scripts doing bulk password resets with weak passwords; TOCTOU races the friendly pre-checks cannot fully close (the comments note the pre-check can lose a race).
Related errors
- No external provider config fields provided
- str(e)
- The system user cannot be deleted, deactivated, promoted to
- Cannot remove the last administrator
- str(e) (ValueError from user service delete, e.g. LastAdmini
AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29).
Data as JSON: /api/errors/9f2eebca7c23ecdf.
Report an issue: GitHub.