{"record":{"id":"0b79336734ff477c","repo":"usestrix/strix","slug":"no-mount-confirmation-is-pending","errorCode":null,"errorMessage":"No mount confirmation is pending","messagePattern":"No mount confirmation is pending","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"warning","filePath":"strix/interface/tui/backend/controller.py","lineNumber":347,"sourceCode":"        return {\"started\": True}\n\n    async def _begin_scan(self, verify: bool) -> None:\n        if self._on_start is None:\n            raise RuntimeError(\"Scan start is unavailable\")\n        self._start_in_progress = True\n        try:\n            await self._on_start(verify)\n        finally:\n            self._start_in_progress = False\n        self.setup_mode = False\n        self.scan_started = True\n        self.scan_state = \"running\"\n\n    async def _confirm_mount(self, payload: dict[str, Any]) -> dict[str, Any]:\n        \"\"\"Answer the pending working-directory mount asked for in the live view.\"\"\"\n        mount = self.pending_workspace_mount\n        if mount is None:\n            raise RuntimeError(\"No mount confirmation is pending\")\n        approved = payload.get(\"approved\")\n        if not isinstance(approved, bool):\n            raise TypeError(\"approved must be a boolean\")\n        self.pending_workspace_mount = None\n        # Declining skips the mount, it does not abandon the scan. The prompt is\n        # the whole of the input either way; the working directory is only an\n        # extra the agent may look at, so the run goes ahead without one.\n        self.workspace_mount = mount if approved else None\n        await self._begin_scan(self._pending_verify)\n        return {\"approved\": approved}\n\n    async def _send_message(self, payload: dict[str, Any]) -> dict[str, Any]:\n        agent_id = self._required_string(payload, \"agent_id\")\n        message = self._required_string(payload, \"message\")\n        if self.coordinator is None:\n            raise RuntimeError(\"Agent coordinator is unavailable\")\n        if self.scan_loop is None or self.scan_loop.is_closed():\n            raise RuntimeError(\"Scan loop is not ready\")","sourceCodeStart":329,"sourceCodeEnd":365,"githubUrl":"https://github.com/usestrix/strix/blob/85513391305171ecc6faffe03da4a8bda5e3febb/strix/interface/tui/backend/controller.py#L329-L365","documentation":"Raised by _confirm_mount (strix/interface/tui/backend/controller.py:347) when a setup.confirm_mount command arrives but pending_workspace_mount is None — i.e. no working-directory mount question is outstanding. The handler exists solely to answer the one-shot confirmation that _start raised; answering twice, or answering before any question was asked, is a state-machine violation.","triggerScenarios":"Sending setup.confirm_mount twice (double-answer of the dialog); sending it before a target-free setup.start with mount_working_dir=true; frontend replaying queued commands after a state reset.","commonSituations":"Dialog double-submit from Enter key repeat; message queues redelivering the confirmation; clients that unconditionally send confirm_mount on every state change.","solutions":["Send setup.confirm_mount only in response to the pending-mount prompt; after the first answer the state is consumed.","Debounce the confirm dialog in the frontend and disable it after one submit.","For drivers, track whether a mount prompt is outstanding (exposed via state) before answering."],"exampleFix":"// before\ndialog.onConfirm = () => send(\"setup.confirm_mount\", { approved });  // fires twice on key-repeat\n\n// after\nlet answered = false;\ndialog.onConfirm = () => { if (!answered) { answered = true; send(\"setup.confirm_mount\", { approved }); } };","handlingStrategy":"validation","validationCode":"if controller.pending_workspace_mount is None:\n    print('no mount prompt outstanding; skip setup.confirm_mount')\nelse:\n    await controller.handle(\"setup.confirm_mount\", {\"approved\": approved})","typeGuard":"def mount_confirmation_pending(controller) -> bool:\n    return getattr(controller, 'pending_workspace_mount', None) is not None","tryCatchPattern":"try:\n    await controller.handle(\"setup.confirm_mount\", payload)\nexcept RuntimeError as exc:\n    if 'No mount confirmation is pending' in str(exc):\n        pass  # duplicate/late answer; nothing to confirm\n    else:\n        raise","preventionTips":["One-shot the confirm dialog: disable it after the first answer.","Only send confirm_mount when the UI actually shows the mount prompt.","Ignore stale queued confirmations after state resets."],"tags":["tui","state-machine","confirmation","reentrancy"],"backgroundTag":null,"analyzedSha":"85513391305171ecc6faffe03da4a8bda5e3febb","analyzedAt":"2026-08-15T05:03:57.275Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}