{"record":{"id":"9d06930acf633916","repo":"invoke-ai/InvokeAI","slug":"last-admin-detail","errorCode":null,"errorMessage":"LAST_ADMIN_DETAIL","messagePattern":"LAST_ADMIN_DETAIL","errorType":"exception","errorClass":"LastAdministratorError","httpStatus":null,"severity":"error","filePath":"invokeai/app/services/users/users_default.py","lineNumber":409,"sourceCode":"        being ``None``, which would also describe a rename. Only a change that actually\n        revokes administrator status is checked, so renaming the last admin stays allowed.\n        \"\"\"\n        cursor.execute(\"SELECT is_admin, is_active FROM users WHERE user_id = ?\", (user_id,))\n        row = cursor.fetchone()\n        if row is None:\n            return\n\n        # An admin who is already inactive is not counted, so removing them changes nothing.\n        if not (bool(row[0]) and bool(row[1])):\n            return\n\n        if not (is_deleting or is_admin is False or is_active is False):\n            return\n\n        cursor.execute(\"SELECT COUNT(*) FROM users WHERE is_admin = TRUE AND is_active = TRUE\")\n        count_row = cursor.fetchone()\n        if (int(count_row[0]) if count_row else 0) <= 1:\n            raise LastAdministratorError(LAST_ADMIN_DETAIL)\n\n    def _assert_system_user_protected(\n        self,\n        user_id: str,\n        *,\n        is_deleting: bool = False,\n        is_admin: bool | None = None,\n        is_active: bool | None = None,\n        password: str | None = None,\n    ) -> None:\n        \"\"\"Reject changes to the ``system`` account that no legitimate operation needs.\n\n        The system row owns every board, image, workflow, and queue item carried over from\n        before multiuser support. Deleting or deactivating it strands all of that: queued\n        items are rejected at dequeue and media reads and saves raise ``PermissionError``.\n\n        Promotion and password-setting are refused for a different reason. The system row\n        is active but has an empty password hash, so it can never authenticate — yet","sourceCodeStart":391,"sourceCodeEnd":427,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/users/users_default.py#L391-L427","documentation":"_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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nusers.update(last_admin_id, UserUpdateRequest(is_admin=False))  # LastAdministratorError\n// after\nusers.update(other_user_id, UserUpdateRequest(is_admin=True))   # ensure a successor\nusers.update(last_admin_id, UserUpdateRequest(is_admin=False))","handlingStrategy":"try-catch","validationCode":"row = db.run_sql('SELECT COUNT(*) FROM users WHERE is_admin = TRUE AND is_active = TRUE').fetchone()\nactive_admins = int(row[0]) if row else 0\nif active_admins <= 1 and (changes.is_admin is False or changes.is_active is False):\n    raise PolicyError('Promote another admin before demoting the last one')","typeGuard":null,"tryCatchPattern":"from invokeai.app.services.users.users_common import LastAdministratorError\ntry:\n    users.update(user_id, changes)\nexcept LastAdministratorError:\n    flash('Cannot remove the last administrator; promote another admin first')\nexcept ValueError as e:\n    raise","preventionTips":["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"],"tags":["last-admin","business-rule","user-management","policy"],"backgroundTag":"last-admin-protection","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}