home-assistant/core · error · ValueError
System generated users cannot enable multi-factor auth modul
Error message
System generated users cannot enable multi-factor auth module.
What it means
ConfigEntryNotReady-style auth signal: brunt's coordinator maps an HTTP 403 from the Brunt cloud API to ConfigEntryAuthFailed, meaning the stored account credentials/token were rejected and HA should launch the re-auth flow rather than retry data updates.
Source
Thrown at homeassistant/auth/__init__.py:420
await self._store.async_deactivate_user(user)
for refresh_token in list(user.refresh_tokens.values()):
self.async_remove_refresh_token(refresh_token)
async def async_remove_credentials(self, credentials: models.Credentials) -> None:
"""Remove credentials."""
provider = self._async_get_auth_provider(credentials)
if provider is not None and hasattr(provider, "async_will_remove_credentials"):
await provider.async_will_remove_credentials(credentials)
await self._store.async_remove_credentials(credentials)
async def async_enable_user_mfa(
self, user: models.User, mfa_module_id: str, data: Any
) -> None:
"""Enable a multi-factor auth module for user."""
if user.system_generated:
raise ValueError(
"System generated users cannot enable multi-factor auth module."
)
if (module := self.get_auth_mfa_module(mfa_module_id)) is None:
raise ValueError(f"Unable find multi-factor auth module: {mfa_module_id}")
await module.async_setup_user(user.id, data)
async def async_disable_user_mfa(
self, user: models.User, mfa_module_id: str
) -> None:
"""Disable a multi-factor auth module for user."""
if user.system_generated:
raise ValueError(
"System generated users cannot disable multi-factor auth module."
)
if (module := self.get_auth_mfa_module(mfa_module_id)) is None:View on GitHub (pinned to 58a3fdb3ea)
Solutions
- Use the re-authenticate flow HA shows for the Brunt entry: Settings > Devices & Services > Brunt > Reauthenticate, and sign in with current credentials.
- If re-auth fails too, verify the username/password work in the Brunt app; change the password if the account is compromised or you suspect a stale credential.
- Check for known issues/updates in the brunt integration and its aionbrt library — an API-side change can make every request 403 until the library is patched.
Example fix
// before
except ClientResponseError as err:
if err.status == 403:
raise ConfigEntryAuthFailed from err
// after: unchanged — correct handling. Optionally add a clearer message:
raise ConfigEntryAuthFailed("Brunt API rejected the stored credentials (HTTP 403)") from err Defensive patterns
Strategy: try-catch
Validate before calling
# There is no cheap pre-check for a 403 — the token's validity is only knowable
# by making the request. Best pre-validation: confirm credentials recently re-authed.
from datetime import datetime, timedelta
def token_likely_fresh(last_auth: datetime) -> bool:
return datetime.now(UTC) - last_auth < timedelta(days=30) Type guard
from aiohttp import ClientResponseError
def is_forbidden(err: BaseException) -> bool:
"""True when the cloud API rejected our credentials (HTTP 403)."""
return isinstance(err, ClientResponseError) and err.status == 403 Try / catch
try:
things = await self.bapi.async_get_things(force=True)
except ClientResponseError as err:
if err.status == 403:
raise ConfigEntryAuthFailed from err # reauth flow
raise UpdateFailed from err # transient, retry next interval Prevention
- Map only 403 to ConfigEntryAuthFailed; mapping 401/5xx too causes spurious reauth prompts.
- Avoid changing the account password without immediately re-authing the integration.
- Log the HTTP status alongside the exception so 403 (auth) is distinguishable from 5xx (outage) in production logs.
When it happens
Trigger: Awaiting self.bapi.async_get_things(force=True) raises aiohttp ClientResponseError with status == 403 — the Brunt API refuses the authenticated request because the session token/API key is invalid, expired, or the account denied access.
Common situations: Password changed on the Brunt account after the token was issued, the OAuth/session token expired server-side, the Brunt cloud service invalidated old tokens after an API change, or rarely the account itself was deactivated.
Related errors
- Brunt not ready to connect.
- Unable to reposition {self._thing.name}
- Unable to deactivate the owner
- Unable find multi-factor auth module: {mfa_module_id}
- Failed to press {button} button.
AI-assisted analysis of home-assistant/core@58a3fdb3ea (2026-08-14).
Data as JSON: /api/errors/6eece3a3ec87e73e.
Report an issue: GitHub.