{"record":{"id":"5f568f5815605d5c","repo":"usestrix/strix","slug":"no-model-configured-set-strix-llm-first","errorCode":null,"errorMessage":"No model configured. Set STRIX_LLM first.","messagePattern":"No model configured\\. Set STRIX_LLM first\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"critical","filePath":"strix/interface/tui/backend/controller.py","lineNumber":313,"sourceCode":"        return {\"instruction\": self.instruction}\n\n    async def _start(self, payload: dict[str, Any]) -> dict[str, Any]:\n        if self.scan_started or self._start_in_progress:\n            raise RuntimeError(\"Scan is already starting or running\")\n        # A bare prompt launches optimistically, like a coding agent: it skips\n        # the network model preflight and surfaces any model error live. A named\n        # target keeps the preflight so a real scan does not commit blind.\n        verify = payload.get(\"verify\", True)\n        if not isinstance(verify, bool):\n            raise TypeError(\"verify must be a boolean\")\n        # Launching with no target mounts the working directory, so it requires\n        # the user's explicit confirmation rather than happening silently.\n        mount_working_dir = payload.get(\"mount_working_dir\", False)\n        if not isinstance(mount_working_dir, bool):\n            raise TypeError(\"mount_working_dir must be a boolean\")\n        model = (load_settings().llm.model or \"\").strip()\n        if not model:\n            raise ValueError(\"No model configured. Set STRIX_LLM first.\")\n        if self._on_start is None:\n            raise RuntimeError(\"Scan start is unavailable\")\n        if not self.targets:\n            if not mount_working_dir:\n                raise ValueError(\"No target set. Add a target first.\")\n            # Mounting the working directory needs the user's confirmation, and\n            # that is asked in the live view. Enter it now and prepare nothing\n            # until the answer arrives, so declining leaves no run behind.\n            self.pending_workspace_mount = str(Path.cwd())\n            self._pending_verify = verify\n            self.setup_mode = False\n            self.scan_started = True\n            self.scan_state = \"preparing\"\n            return {\"started\": True}\n        await self._begin_scan(verify)\n        return {\"started\": True}\n\n    async def _begin_scan(self, verify: bool) -> None:","sourceCodeStart":295,"sourceCodeEnd":331,"githubUrl":"https://github.com/usestrix/strix/blob/85513391305171ecc6faffe03da4a8bda5e3febb/strix/interface/tui/backend/controller.py#L295-L331","documentation":"Raised by _start in the TUI controller (strix/interface/tui/backend/controller.py:313) when load_settings().llm.model is empty at start time — the TUI equivalent of the runner's model check. The interactive scan needs a LiteLLM model id (normally from STRIX_LLM or persisted settings) before it can launch, and the controller fails fast with ValueError instead of starting a doomed scan.","triggerScenarios":"Opening the TUI and pressing start in a shell without STRIX_LLM exported and with no llm.model in ~/.strix/cli-config.json; a config file whose llm section was wiped; env lost when launching via desktop launcher/sudo.","commonSituations":"Same root causes as error 22 but surfaced in the TUI: fresh installs, stripped environments in CI/desktop launchers, subshell exports.","solutions":["Export STRIX_LLM=<model-id> in the launching shell, then restart the TUI.","Persist the model in ~/.strix/cli-config.json (llm.model) so every future session has it.","Confirm LLM_API_KEY is also set for the chosen provider before starting.","After fixing, retry start from the setup page — no restart of the scan state needed since it failed pre-start."],"exampleFix":"# before\n$ strix   # TUI -> start -> ValueError: No model configured\n\n# after\n$ export STRIX_LLM=\"anthropic/claude-sonnet-4\"\n$ export LLM_API_KEY=\"sk-ant-...\"\n$ strix","handlingStrategy":"validation","validationCode":"import os\nfrom strix.config import load_settings\nmodel = (load_settings().llm.model or os.environ.get('STRIX_LLM') or '').strip()\nif not model:\n    print('configure STRIX_LLM before entering the TUI setup page')","typeGuard":"def model_ready_for_tui() -> bool:\n    import os\n    from strix.config import load_settings\n    return bool((load_settings().llm.model or os.environ.get('STRIX_LLM') or '').strip())","tryCatchPattern":"try:\n    await controller.handle(\"setup.start\", {})\nexcept ValueError as exc:\n    if 'No model configured' in str(exc):\n        show_setup_error('Set STRIX_LLM and retry start')\n    else:\n        raise","preventionTips":["Export STRIX_LLM in the shell/profile that launches the TUI.","Persist llm.model in ~/.strix/cli-config.json.","Surface this as a setup-page hint before the user presses start."],"tags":["tui","configuration","llm","environment"],"backgroundTag":null,"analyzedSha":"85513391305171ecc6faffe03da4a8bda5e3febb","analyzedAt":"2026-08-15T05:03:57.275Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}