{"record":{"id":"2d093bc346eedc0d","repo":"home-assistant/core","slug":"wake-word-interception-already-in-progress","errorCode":null,"errorMessage":"Wake word interception already in progress","messagePattern":"Wake word interception already in progress","errorType":"exception","errorClass":"SatelliteBusyError","httpStatus":null,"severity":"warning","filePath":"homeassistant/components/assist_satellite/entity.py","lineNumber":186,"sourceCode":"\n    @callback\n    @abstractmethod\n    def async_get_configuration(self) -> AssistSatelliteConfiguration:\n        \"\"\"Get the current satellite configuration.\"\"\"\n\n    @abstractmethod\n    async def async_set_configuration(\n        self, config: AssistSatelliteConfiguration\n    ) -> None:\n        \"\"\"Set the current satellite configuration.\"\"\"\n\n    async def async_intercept_wake_word(self) -> str | None:\n        \"\"\"Intercept the next wake word from the satellite.\n\n        Returns the detected wake word phrase or None.\n        \"\"\"\n        if self._wake_word_intercept_future is not None:\n            raise SatelliteBusyError(\"Wake word interception already in progress\")\n\n        # Will cause next wake word to be intercepted in\n        # async_accept_pipeline_from_satellite\n        self._wake_word_intercept_future = asyncio.Future()\n\n        _LOGGER.debug(\"Next wake word will be intercepted: %s\", self.entity_id)\n\n        try:\n            return await self._wake_word_intercept_future\n        finally:\n            self._wake_word_intercept_future = None\n\n    async def async_internal_announce(\n        self,\n        message: str | None = None,\n        media_id: str | None = None,\n        preannounce: bool = True,\n        preannounce_media_id: str = PREANNOUNCE_URL,","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/home-assistant/core/blob/58a3fdb3ea0538617f0a07efcfba6294de64fd59/homeassistant/components/assist_satellite/entity.py#L168-L204","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Await or cancel the first interception before starting another; the finally-block resets the future, so ensure the prior coroutine finished","Add a timeout around the first interception (e.g. asyncio.wait_for) so it cannot hold the slot indefinitely","Serialize interception calls behind an asyncio.Lock or check the entity's interception state before calling","If stuck, reloading the integration or restarting the device clears the pending future"],"exampleFix":"# before\nword1 = await entity.async_intercept_wake_word()\nword2 = await entity.async_intercept_wake_word()  # may run concurrently -> SatelliteBusyError\n\n# after\nasync with interception_lock:\n    word = await asyncio.wait_for(\n        entity.async_intercept_wake_word(), timeout=30\n    )","handlingStrategy":"validation","validationCode":"if entity._wake_word_intercept_future is not None:  # interception pending\n    _LOGGER.debug(\"Skipping: wake word interception already active for %s\", entity.entity_id)","typeGuard":null,"tryCatchPattern":"try:\n    word = await asyncio.wait_for(entity.async_intercept_wake_word(), timeout=30)\nexcept SatelliteBusyError:\n    # another interception owns the slot; skip or wait for it to finish\n    word = None\nexcept asyncio.TimeoutError:\n    word = None","preventionTips":["Wrap interception in asyncio.wait_for so the slot is always released","Serialize async_intercept_wake_word calls per entity behind a lock","Never fire-and-forget interception tasks; always await or cancel them"],"tags":["assist-satellite","concurrency","voice","wake-word"],"backgroundTag":null,"analyzedSha":"58a3fdb3ea0538617f0a07efcfba6294de64fd59","analyzedAt":"2026-08-14T20:54:38.818Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}