home-assistant/core · error · InvalidConfigEntryID

Config entry {config_entry_id} not found

Error message

Config entry {config_entry_id} not found

What it means

InvalidConfigEntryID raised by config_entry_id_to_source: hass.config_entries.async_get_entry(config_entry_id) returned nothing, so the passed id does not correspond to any existing config entry (deleted, never existed, or typo'd). The function exists to turn a Bluetooth config entry id into a scanner source address.

Source

Thrown at homeassistant/components/bluetooth/util.py:131

            ):
                connectable_loaded_history[address] = service_info

    return all_loaded_history, connectable_loaded_history


@callback
def adapter_title(adapter: str, details: AdapterDetails) -> str:
    """Return the adapter title."""
    unique_name = adapter_unique_name(adapter, details[ADAPTER_ADDRESS])
    model = details.get(ADAPTER_PRODUCT, "Unknown")
    manufacturer = details[ADAPTER_MANUFACTURER] or "Unknown"
    return f"{manufacturer} {model} ({unique_name})"


def config_entry_id_to_source(hass: HomeAssistant, config_entry_id: str) -> str:
    """Convert a config entry id to a source."""
    if not (entry := hass.config_entries.async_get_entry(config_entry_id)):
        raise InvalidConfigEntryID(f"Config entry {config_entry_id} not found")
    source = entry.unique_id
    assert source is not None
    if not get_manager().async_scanner_by_source(source):
        raise InvalidSource(f"Source {source} not found")
    return source

View on GitHub (pinned to 58a3fdb3ea)

Solutions

  1. Re-fetch the current config entry id (Settings > Devices & Services, or config_entries API) and pass that
  2. If the entry was deleted intentionally, remove the automation/script/UI element that still stores the old id
  3. Catch InvalidConfigEntryID and re-enumerate entries instead of retrying the same id

Example fix

# before
source = config_entry_id_to_source(hass, stale_entry_id)

# after
entry = hass.config_entries.async_entries(bluetooth.__dict__['DOMAIN'])[0]
source = config_entry_id_to_source(hass, entry.entry_id)
Defensive patterns

Strategy: validation

Validate before calling

def config_entry_exists(hass, config_entry_id: str) -> bool:
    return hass.config_entries.async_get_entry(config_entry_id) is not None

Try / catch

from homeassistant.components.bluetooth.util import InvalidConfigEntryID
try:
    source = config_entry_id_to_source(hass, entry_id)
except InvalidConfigEntryID:
    entry_id = hass.config_entries.async_entries("bluetooth")[0].entry_id  # re-resolve
    source = config_entry_id_to_source(hass, entry_id)

Prevention

When it happens

Trigger: Calling config_entry_id_to_source (directly or via a websocket/API that accepts a config_entry_id) with an id from a stale UI session after the entry was removed, a truncated id, or a random string.

Common situations: Frontend dialog holding an entry id across a reload/delete; scripts or blueprints referencing an entry that was re-created with a new id; copy-paste of an id with missing characters.

Related errors


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