{"record":{"id":"11a05a89f393e7df","repo":"usestrix/strix","slug":"setup-can-no-longer-be-changed-after-the-scan-star","errorCode":null,"errorMessage":"Setup can no longer be changed after the scan starts","messagePattern":"Setup can no longer be changed after the scan starts","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"warning","filePath":"strix/interface/tui/backend/controller.py","lineNumber":488,"sourceCode":"            httpd.server_close()\n\n    async def _quit(self, _payload: dict[str, Any]) -> dict[str, Any]:\n        self.close_viewer()\n        if self._on_quit is not None:\n            await self._on_quit()\n        self.scan_state = \"stopped\"\n        return {\"quitting\": True}\n\n    @staticmethod\n    def _required_string(payload: dict[str, Any], name: str) -> str:\n        value = payload.get(name)\n        if not isinstance(value, str) or not value.strip():\n            raise ValueError(f\"{name} must be a non-empty string\")\n        return value.strip()\n\n    def _require_setup_mutable(self) -> None:\n        if not self.setup_mode or self.scan_started or self._start_in_progress:\n            raise RuntimeError(\"Setup can no longer be changed after the scan starts\")\n","sourceCodeStart":470,"sourceCodeEnd":489,"githubUrl":"https://github.com/usestrix/strix/blob/85513391305171ecc6faffe03da4a8bda5e3febb/strix/interface/tui/backend/controller.py#L470-L489","documentation":"Raised by _require_setup_mutable (controller.py:488) when a setup-mutating command (changing targets, options, credentials) arrives after the setup phase ended. The guard trips when setup_mode is false, or scan_started is true, or _start_in_progress is true — any of these makes configuration immutable. Strix freezes configuration at scan start so the running scan cannot observe mid-flight config changes.","triggerScenarios":"Calling any setup-mutating handler (target add/remove, option change) after 'Start scan' was pressed (_start_in_progress or scan_started), or after setup_mode was cleared. Includes the window between clicking start and the scan actually launching.","commonSituations":"UI lets the user edit the target list while the scan is starting; late-arriving keystrokes queued before start; users trying to retarget a running scan instead of restarting it; race where the setup screen is still rendered but the start already fired.","solutions":["Treat setup as locked once start is clicked: disable setup controls on _start_in_progress","To change configuration, quit and start a new scan session","Check controller.setup_mode / scan_started before sending setup commands","In automation, complete all configuration before invoking the start command"],"exampleFix":"# before\nawait controller._set_target({\"target\": \"https://example.com\"})  # after start\n\n# after\nif controller.setup_mode and not controller.scan_started:\n    await controller._set_target({\"target\": \"https://example.com\"})\nelse:\n    logging.warning(\"setup locked; restart the scan to reconfigure\")","handlingStrategy":"validation","validationCode":"if not controller.setup_mode or controller.scan_started or controller._start_in_progress:\n    raise RuntimeError(\"setup locked; restart the scan to reconfigure\")","typeGuard":"def setup_mutable(c) -> bool:\n    return c.setup_mode and not c.scan_started and not c._start_in_progress","tryCatchPattern":"try:\n    await controller.dispatch(setup_command, payload)\nexcept RuntimeError as e:\n    if \"Setup can no longer be changed\" in str(e):\n        prompt_restart_session()\n    else:\n        raise","preventionTips":["Lock setup controls the instant start is clicked, not when the scan flips state","Complete all configuration before sending the start command","To reconfigure, start a fresh session rather than fighting the guard"],"tags":["tui","lifecycle","state","configuration"],"backgroundTag":null,"analyzedSha":"85513391305171ecc6faffe03da4a8bda5e3febb","analyzedAt":"2026-08-15T05:03:57.275Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}