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

  1. Use the re-authenticate flow HA shows for the Brunt entry: Settings > Devices & Services > Brunt > Reauthenticate, and sign in with current credentials.
  2. 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.
  3. 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

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


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