home-assistant/core · error · ValueError

{client_name} already exists

Error message

{client_name} already exists

What it means

Thrown by AuthManager.async_create_refresh_token (homeassistant/auth/__init__.py:504) when creating a long-lived access token whose client_name already exists among the user's refresh tokens of the same type. Each client_name may back exactly one long-lived token per user, so duplicates are rejected.

Source

Thrown at homeassistant/auth/__init__.py:504

        if token_type == models.TOKEN_TYPE_NORMAL and client_id is None:
            raise ValueError("Client is required to generate a refresh token.")

        if (
            token_type == models.TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN
            and client_name is None
        ):
            raise ValueError("Client_name is required for long-lived access token")

        if token_type == models.TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN:
            for token in user.refresh_tokens.values():
                if (
                    token.client_name == client_name
                    and token.token_type == models.TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN
                ):
                    # Each client_name can only have one
                    # long_lived_access_token type of refresh token
                    raise ValueError(f"{client_name} already exists")

        return await self._store.async_create_refresh_token(
            user,
            client_id,
            client_name,
            client_icon,
            token_type,
            access_token_expiration,
            expire_at,
            credential,
        )

    @callback
    def async_get_refresh_token(self, token_id: str) -> models.RefreshToken | None:
        """Get refresh token by id."""
        return self._store.async_get_refresh_token(token_id)

    @callback

View on GitHub (pinned to 58a3fdb3ea)

Solutions

  1. Reuse the existing token: search user.refresh_tokens for the client_name and return that token instead of creating a new one
  2. Delete the old token first (hass.auth.async_remove_refresh_token) if rotation is intended
  3. Use a unique name, e.g. append a timestamp or purpose suffix

Example fix

// before
await hass.auth.async_create_refresh_token(
    user, token_type=models.TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN, client_name="script"
)

# after
existing = next(
    (t for t in user.refresh_tokens.values()
     if t.client_name == "script"
     and t.token_type == models.TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN),
    None,
)
if existing is None:
    await hass.auth.async_create_refresh_token(
        user, token_type=models.TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN, client_name="script"
    )
Defensive patterns

Strategy: validation

Validate before calling

existing = next(
    (t for t in user.refresh_tokens.values()
     if t.client_name == client_name
     and t.token_type == models.TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN),
    None,
)
if existing is None:
    await hass.auth.async_create_refresh_token(
        user, token_type=models.TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN, client_name=client_name
    )

Type guard

def name_is_free(user, client_name) -> bool:
    return not any(
        t.client_name == client_name
        and t.token_type == models.TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN
        for t in user.refresh_tokens.values()
    )

Try / catch

try:
    token = await hass.auth.async_create_refresh_token(
        user, token_type=models.TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN, client_name=client_name
    )
except ValueError as err:
    if "already exists" not in str(err):
        raise
    token = next(t for t in user.refresh_tokens.values() if t.client_name == client_name)

Prevention

When it happens

Trigger: Creating a second long-lived token with a name already used by an existing token for that user; re-running a provisioning script that always uses the same name.

Common situations: Idempotency-unaware setup scripts; users creating 'API token' twice from tooling; name collisions after restoring a config backup while the script also creates the token.

Related errors


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