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)
@callbackView on GitHub (pinned to 58a3fdb3ea)
Solutions
- Reuse the existing token: search user.refresh_tokens for the client_name and return that token instead of creating a new one
- Delete the old token first (hass.auth.async_remove_refresh_token) if rotation is intended
- 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
- Make token-provisioning scripts idempotent: look up by client_name before creating
- Rotate by deleting the old token first when you want a fresh secret
- Include a purpose or date suffix in generated client names
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
- Client_name is required for long-lived access token
- User is not active
- System generated users cannot have refresh tokens connected
- System generated users can only have system type refresh tok
- Client is required to generate a refresh token.
AI-assisted analysis of home-assistant/core@58a3fdb3ea (2026-08-14).
Data as JSON: /api/errors/2a521ec934b7081b.
Report an issue: GitHub.