{"record":{"id":"b46b43253e0f2105","repo":"home-assistant/core","slug":"user-not-found","errorCode":"user_not_found","errorMessage":"user_not_found","messagePattern":"user_not_found","errorType":"exception","errorClass":"InvalidUser","httpStatus":null,"severity":"error","filePath":"homeassistant/auth/providers/homeassistant.py","lineNumber":226,"sourceCode":"            {\n                \"username\": username,\n                \"password\": self.hash_password(password, True).decode(),\n            }\n        )\n\n    @callback\n    def async_remove_auth(self, username: str) -> None:\n        \"\"\"Remove authentication.\"\"\"\n        username = self.normalize_username(username)\n\n        index = None\n        for i, user in enumerate(self.users):\n            if self.normalize_username(user[\"username\"]) == username:\n                index = i\n                break\n\n        if index is None:\n            raise InvalidUser(translation_key=\"user_not_found\")\n\n        self.users.pop(index)\n\n    def change_password(self, username: str, new_password: str) -> None:\n        \"\"\"Update the password.\n\n        Raises InvalidUser if user cannot be found.\n        \"\"\"\n        username = self.normalize_username(username)\n\n        for user in self.users:\n            if self.normalize_username(user[\"username\"]) == username:\n                user[\"password\"] = self.hash_password(new_password, True).decode()\n                break\n        else:\n            raise InvalidUser(translation_key=\"user_not_found\")\n\n    @callback","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/home-assistant/core/blob/58a3fdb3ea0538617f0a07efcfba6294de64fd59/homeassistant/auth/providers/homeassistant.py#L208-L244","documentation":"InvalidUser with translation_key user_not_found, raised by Data.async_remove_auth (homeassistant/auth/providers/homeassistant.py:226) when the username to delete does not exist in the local auth provider's user store. Usernames are normalized (strip + casefold) before lookup, so only a truly absent (or differently spelled-but-normalizing-equal) name passes.","triggerScenarios":"Calling provider.data.async_remove_auth(username) for a name not in the storage; name already removed by a prior call; passing the HA user name instead of the provider username.","commonSituations":"Onboarding/offboarding scripts deleting an already-deleted account; mismatch between auth provider usernames and user-facing names; concurrent deletion flows.","solutions":["Check membership first: `if all(prv.normalize_username(u['username']) != provider.data.normalize_username(username) for u in provider.data.users): skip` — or simply treat user_not_found as success (idempotent delete)","Catch InvalidUser and inspect translation_key == 'user_not_found' to tolerate double-delete"],"exampleFix":"// before\nprovider.data.async_remove_auth(username)  # raises if absent\n\n# after\nfrom homeassistant.auth.providers.homeassistant import InvalidUser\ntry:\n    provider.data.async_remove_auth(username)\nexcept InvalidUser as err:\n    if err.translation_key != \"user_not_found\":\n        raise","handlingStrategy":"try-catch","validationCode":"normalized = provider.normalize_username(username)\nexists = any(\n    provider.normalize_username(u[\"username\"]) == normalized\n    for u in provider.data.users\n)\nif exists:\n    provider.data.async_remove_auth(username)","typeGuard":null,"tryCatchPattern":"from homeassistant.auth.providers.homeassistant import InvalidUser\ntry:\n    provider.data.async_remove_auth(username)\nexcept InvalidUser as err:\n    if err.translation_key != \"user_not_found\":\n        raise\n    # already gone: nothing to do","preventionTips":["Treat removal as idempotent: user_not_found on delete is success","Always use the provider's own username list as source of truth"],"tags":["auth","python","home-assistant","user-management","local-provider"],"backgroundTag":null,"analyzedSha":"58a3fdb3ea0538617f0a07efcfba6294de64fd59","analyzedAt":"2026-08-14T20:54:38.818Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}