home-assistant/core · error · InvalidUsername

username_not_normalized

username_not_normalized

Error message

username_not_normalized

What it means

InvalidUsername with translation_key username_not_normalized, raised by Data._validate_new_username (homeassistant/auth/providers/homeassistant.py:254) when the proposed username differs from its force-normalized form (username.strip().casefold()). The provider refuses to store names with leading/trailing whitespace or uppercase letters so lookups stay deterministic; the offending value is passed as the new_username placeholder.

Source

Thrown at homeassistant/auth/providers/homeassistant.py:254

        for user in self.users:
            if self.normalize_username(user["username"]) == username:
                user["password"] = self.hash_password(new_password, True).decode()
                break
        else:
            raise InvalidUser(translation_key="user_not_found")

    @callback
    def _validate_new_username(self, new_username: str) -> None:
        """Validate that username is normalized and unique.

        Raises InvalidUsername if the new username is invalid.
        """
        normalized_username = self.normalize_username(
            new_username, force_normalize=True
        )
        if normalized_username != new_username:
            raise InvalidUsername(
                translation_key="username_not_normalized",
                translation_placeholders={"new_username": new_username},
            )

        if any(
            self.normalize_username(user["username"]) == normalized_username
            for user in self.users
        ):
            raise InvalidUsername(
                translation_key="username_already_exists",
                translation_placeholders={"username": new_username},
            )

    @callback
    def change_username(self, username: str, new_username: str) -> None:
        """Update the username.

        Raises InvalidUser if user cannot be found.

View on GitHub (pinned to 58a3fdb3ea)

Solutions

  1. Normalize before calling: `username = username.strip().casefold()`
  2. Enforce trim + lowercase in the UI/form layer for new usernames

Example fix

// before
provider.data.add_auth("Bob", password)  # raises username_not_normalized

# after
provider.data.add_auth("bob", password)
Defensive patterns

Strategy: validation

Validate before calling

new_username = new_username.strip().casefold()
provider.data.add_auth(new_username, password)

Type guard

def is_normalized(username: str) -> bool:
    return username == username.strip().casefold()

Try / catch

from homeassistant.auth.providers.homeassistant import InvalidUsername
try:
    provider.data.add_auth(username, password)
except InvalidUsername as err:
    if err.translation_key != "username_not_normalized":
        raise
    provider.data.add_auth(username.strip().casefold(), password)

Prevention

When it happens

Trigger: Calling add_auth or change_username with e.g. " Alice ", "Bob", or any mixed-case/whitespace name; usernames sourced from external directories that preserve case.

Common situations: Programmatic user creation using display names; migrating old user lists; input from forms that don't trim/casefold before submission.

Related errors


AI-assisted analysis of home-assistant/core@58a3fdb3ea (2026-08-14). Data as JSON: /api/errors/8e643fdc61653600. Report an issue: GitHub.