home-assistant/core · warning · SatelliteBusyError

Wake word interception already in progress

Error message

Wake word interception already in progress

What it means

SatelliteBusyError (a HomeAssistantError subclass) thrown by AssistSatelliteEntity.async_intercept_wake_word when an interception is already pending. The entity tracks a single _wake_word_intercept_future; a second concurrent call sees it non-None and aborts rather than queueing.

Source

Thrown at homeassistant/components/assist_satellite/entity.py:186

    @callback
    @abstractmethod
    def async_get_configuration(self) -> AssistSatelliteConfiguration:
        """Get the current satellite configuration."""

    @abstractmethod
    async def async_set_configuration(
        self, config: AssistSatelliteConfiguration
    ) -> None:
        """Set the current satellite configuration."""

    async def async_intercept_wake_word(self) -> str | None:
        """Intercept the next wake word from the satellite.

        Returns the detected wake word phrase or None.
        """
        if self._wake_word_intercept_future is not None:
            raise SatelliteBusyError("Wake word interception already in progress")

        # Will cause next wake word to be intercepted in
        # async_accept_pipeline_from_satellite
        self._wake_word_intercept_future = asyncio.Future()

        _LOGGER.debug("Next wake word will be intercepted: %s", self.entity_id)

        try:
            return await self._wake_word_intercept_future
        finally:
            self._wake_word_intercept_future = None

    async def async_internal_announce(
        self,
        message: str | None = None,
        media_id: str | None = None,
        preannounce: bool = True,
        preannounce_media_id: str = PREANNOUNCE_URL,

View on GitHub (pinned to 58a3fdb3ea)

Solutions

  1. Await or cancel the first interception before starting another; the finally-block resets the future, so ensure the prior coroutine finished
  2. Add a timeout around the first interception (e.g. asyncio.wait_for) so it cannot hold the slot indefinitely
  3. Serialize interception calls behind an asyncio.Lock or check the entity's interception state before calling
  4. If stuck, reloading the integration or restarting the device clears the pending future

Example fix

# before
word1 = await entity.async_intercept_wake_word()
word2 = await entity.async_intercept_wake_word()  # may run concurrently -> SatelliteBusyError

# after
async with interception_lock:
    word = await asyncio.wait_for(
        entity.async_intercept_wake_word(), timeout=30
    )
Defensive patterns

Strategy: validation

Validate before calling

if entity._wake_word_intercept_future is not None:  # interception pending
    _LOGGER.debug("Skipping: wake word interception already active for %s", entity.entity_id)

Try / catch

try:
    word = await asyncio.wait_for(entity.async_intercept_wake_word(), timeout=30)
except SatelliteBusyError:
    # another interception owns the slot; skip or wait for it to finish
    word = None
except asyncio.TimeoutError:
    word = None

Prevention

When it happens

Trigger: Two concurrent calls to async_intercept_wake_word on the same entity (e.g. two assist pipelines or a script overlapping a previous interception that has not resolved yet); the previous interception never completed because no wake word was spoken and no timeout/cancel occurred.

Common situations: Voice assistant tuning scripts that repeatedly request interception without awaiting or cancelling; a pipeline stuck waiting for a wake word while another flow starts; UI 'test wake word' pressed twice.

Related errors


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