home-assistant/core · error · ValueError
System generated users cannot disable multi-factor auth modu
Error message
System generated users cannot disable multi-factor auth module.
What it means
ConfigEntryNotReady in bryant_evolution signals that the HVAC gateway (Evolution gateway accessed over a local serial/USB file) was not reachable at setup, so HA should retry later. It is raised two ways: explicitly when a probe via _can_reach_device(client) fails, and from FileNotFoundError when BryantEvolutionLocalClient.get_client cannot open the configured serial device file.
Source
Thrown at homeassistant/auth/__init__.py:434
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:
raise ValueError(f"Unable find multi-factor auth module: {mfa_module_id}")
await module.async_depose_user(user.id)
async def async_get_enabled_mfa(self, user: models.User) -> dict[str, str]:
"""List enabled mfa modules for user."""
modules: dict[str, str] = OrderedDict()
for module_id, module in self._mfa_modules.items():
if await module.async_is_user_setup(user.id):
modules[module_id] = module.name
return modules
async def async_create_refresh_token(
self,View on GitHub (pinned to 58a3fdb3ea)
Solutions
- Verify the device file in the config entry exists and is stable: `ls -l /dev/ttyUSB*` on the machine running HA; if the node changed, update CONF_FILENAME or add a udev rule to pin a stable symlink.
- Check permissions: the Home Assistant process user needs rw access to the tty (dialout group on HassOS/Debian); test with `ls -l /dev/ttyUSB0` and fix group membership or the udev rule.
- Confirm the Bryant/Carrier Evolution gateway is powered and connected (serial cable seated, zone numbers in CONF_SYSTEM_ZONE match the installed system/zone).
- Do nothing for transient cases — HA retries setup with backoff; once the adapter/gateway comes back the entry sets up successfully.
Example fix
// before
client = await BryantEvolutionLocalClient.get_client(
sz[0], sz[1], entry.data[CONF_FILENAME]
)
if not await _can_reach_device(client):
raise ConfigEntryNotReady
// after (user-side): pin a stable device symlink via udev instead of raw ttyUSBx:
# /etc/udev/rules.d/99-bryant.rules
# SUBSYSTEM=="tty", ATTRS{idVendor}=="xxxx", ATTRS{idProduct}=="yyyy", SYMLINK+="bryant_evolution"
# then configure CONF_FILENAME as /dev/bryant_evolution Defensive patterns
Strategy: validation
Validate before calling
# Before setup: verify the serial device node exists and is readable/writable
import os, pathlib
def serial_device_ok(filename: str) -> bool:
p = pathlib.Path(filename)
return p.exists() and os.access(p, os.R_OK | os.W_OK) Type guard
def is_missing_device(err: BaseException) -> bool:
"""True when the local serial device path does not exist."""
return isinstance(err, FileNotFoundError) Try / catch
for sz in entry.data[CONF_SYSTEM_ZONE]:
try:
client = await BryantEvolutionLocalClient.get_client(sz[0], sz[1], entry.data[CONF_FILENAME])
if not await _can_reach_device(client):
raise ConfigEntryNotReady
entry.runtime_data[tuple(sz)] = client
except FileNotFoundError as f:
raise ConfigEntryNotReady from f # retry when the tty reappears Prevention
- Use a udev rule with a stable SYMLINK (e.g. /dev/bryant_evolution) instead of /dev/ttyUSB0 so reboots/replugs don't change the path.
- Ensure the HA process user has rw permission on the tty (dialout group) before setup.
- Keep CONF_SYSTEM_ZONE values in sync with the actual Evolution system/zone addressing.
- Expect ConfigEntryNotReady during adapter/gateway power loss — HA's backoff retries make it self-healing once hardware returns.
When it happens
Trigger: Calling BryantEvolutionLocalClient.get_client(system, zone, entry.data[CONF_FILENAME]) raises FileNotFoundError because CONF_FILENAME (a tty/USB device path like /dev/ttyUSB0) does not exist; or the client is created but `await _can_reach_device(client)` returns falsy (no response from the Evolution system/zone), triggering the bare `raise ConfigEntryNotReady`.
Common situations: USB-to-serial adapter re-enumerated to a different /dev node (ttyUSB0 → ttyUSB1) after reboot or replug, udev permissions deny access to the tty (file absent for the HA user), the gateway/bryant bridge is powered off or its serial cable disconnected, or the configured system/zone numbers don't correspond to a reachable zone.
Related errors
- Credential is already linked to a user
- Invalid authentication
- wrong_file_path
- Cannot delete credential in use by integration {entry.domain
- Authentication failed, please check credentials
AI-assisted analysis of home-assistant/core@58a3fdb3ea (2026-08-14).
Data as JSON: /api/errors/03e0465a022f902d.
Report an issue: GitHub.