{"record":{"id":"2dc53bc23fb26b09","repo":"home-assistant/core","slug":"username-already-exists","errorCode":"username_already_exists","errorMessage":"username_already_exists","messagePattern":"username_already_exists","errorType":"exception","errorClass":"InvalidUsername","httpStatus":null,"severity":"error","filePath":"homeassistant/auth/providers/homeassistant.py","lineNumber":263,"sourceCode":"    def _validate_new_username(self, new_username: str) -> None:\n        \"\"\"Validate that username is normalized and unique.\n\n        Raises InvalidUsername if the new username is invalid.\n        \"\"\"\n        normalized_username = self.normalize_username(\n            new_username, force_normalize=True\n        )\n        if normalized_username != new_username:\n            raise InvalidUsername(\n                translation_key=\"username_not_normalized\",\n                translation_placeholders={\"new_username\": new_username},\n            )\n\n        if any(\n            self.normalize_username(user[\"username\"]) == normalized_username\n            for user in self.users\n        ):\n            raise InvalidUsername(\n                translation_key=\"username_already_exists\",\n                translation_placeholders={\"username\": new_username},\n            )\n\n    @callback\n    def change_username(self, username: str, new_username: str) -> None:\n        \"\"\"Update the username.\n\n        Raises InvalidUser if user cannot be found.\n        Raises InvalidUsername if the new username is invalid.\n        \"\"\"\n        username = self.normalize_username(username)\n        self._validate_new_username(new_username)\n\n        for user in self.users:\n            if self.normalize_username(user[\"username\"]) == username:\n                user[\"username\"] = new_username\n                assert self._data is not None","sourceCodeStart":245,"sourceCodeEnd":281,"githubUrl":"https://github.com/home-assistant/core/blob/58a3fdb3ea0538617f0a07efcfba6294de64fd59/homeassistant/auth/providers/homeassistant.py#L245-L281","documentation":"InvalidUsername with translation_key username_already_exists, raised by Data._validate_new_username (homeassistant/auth/providers/homeassistant.py:263) when the normalized new username equals an existing stored username (comparison is done on normalized forms, so 'Bob' collides with 'bob'). The placeholder username carries the rejected value.","triggerScenarios":"add_auth with a name already in the store; change_username to a name owned by another account; migration scripts re-creating existing users.","commonSituations":"Idempotent provisioning scripts that don't check first; renames colliding with another user; imports merging two sources with duplicate names.","solutions":["Before creating, check `any(provider.data.normalize_username(u[\"username\"]) == new_username.strip().casefold() for u in provider.data.users)` and skip","For renames, first verify the target name is free; for imports, deduplicate names before writing"],"exampleFix":"// before\nprovider.data.add_auth(\"alice\", password)  # alice exists\n\n# after\nnormalized = \"alice\"\nif not any(provider.data.normalize_username(u[\"username\"]) == normalized for u in provider.data.users):\n    provider.data.add_auth(normalized, password)","handlingStrategy":"validation","validationCode":"normalized = new_username.strip().casefold()\nif not any(provider.data.normalize_username(u[\"username\"]) == normalized for u in provider.data.users):\n    provider.data.add_auth(new_username, password)","typeGuard":"def username_is_unique(provider_data, new_username: str) -> bool:\n    normalized = new_username.strip().casefold()\n    return not any(\n        provider_data.normalize_username(u[\"username\"]) == normalized\n        for u in provider_data.users\n    )","tryCatchPattern":"from homeassistant.auth.providers.homeassistant import InvalidUsername\ntry:\n    provider.data.add_auth(username, password)\nexcept InvalidUsername as err:\n    if err.translation_key != \"username_already_exists\":\n        raise\n    # pick a different name or return the existing account","preventionTips":["Check name availability with a normalized comparison before creating","Deduplicate names during imports and migrations"],"tags":["auth","python","home-assistant","username","duplicate","local-provider"],"backgroundTag":null,"analyzedSha":"58a3fdb3ea0538617f0a07efcfba6294de64fd59","analyzedAt":"2026-08-14T20:54:38.818Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}