usestrix/strix · warning · RuntimeError

Scan is already starting or running

Error message

Scan is already starting or running

What it means

Raised by the TUI controller's _start handler (strix/interface/tui/backend/controller.py:299) when a setup.start command arrives while scan_started is already true or _start_in_progress is true. It is a re-entrancy guard: one scan per controller instance, and the async start sequence (which awaits _begin_scan) must not run twice concurrently.

Source

Thrown at strix/interface/tui/backend/controller.py:299

    async def _add_target(self, payload: dict[str, Any]) -> dict[str, Any]:
        self._require_setup_mutable()
        target = self._required_string(payload, "target")
        if target not in self.targets:
            self.targets.append(target)
        return {"target": target, "total": len(self.targets)}

    async def _set_instruction(self, payload: dict[str, Any]) -> dict[str, Any]:
        self._require_setup_mutable()
        instruction = payload.get("instruction", "")
        if not isinstance(instruction, str):
            raise TypeError("instruction must be a string")
        self.instruction = instruction.strip()
        return {"instruction": self.instruction}

    async def _start(self, payload: dict[str, Any]) -> dict[str, Any]:
        if self.scan_started or self._start_in_progress:
            raise RuntimeError("Scan is already starting or running")
        # A bare prompt launches optimistically, like a coding agent: it skips
        # the network model preflight and surfaces any model error live. A named
        # target keeps the preflight so a real scan does not commit blind.
        verify = payload.get("verify", True)
        if not isinstance(verify, bool):
            raise TypeError("verify must be a boolean")
        # Launching with no target mounts the working directory, so it requires
        # the user's explicit confirmation rather than happening silently.
        mount_working_dir = payload.get("mount_working_dir", False)
        if not isinstance(mount_working_dir, bool):
            raise TypeError("mount_working_dir must be a boolean")
        model = (load_settings().llm.model or "").strip()
        if not model:
            raise ValueError("No model configured. Set STRIX_LLM first.")
        if self._on_start is None:
            raise RuntimeError("Scan start is unavailable")
        if not self.targets:
            if not mount_working_dir:

View on GitHub (pinned to 8551339130)

Solutions

  1. Treat it as benign in the UI: the first start is still progressing — do not retry, wait for the state change to 'preparing'/'running'.
  2. Debounce/guard the start button in the frontend (disable once clicked until scan_started flips).
  3. For programmatic drivers, poll a status/read endpoint and only send setup.start when no scan is active.

Example fix

// before
button.onClick = () => send("setup.start", {});   // double-click -> RuntimeError

// after
let started = false;
button.onClick = () => { if (!started) { started = true; send("setup.start", {}); } };
Defensive patterns

Strategy: try-catch

Validate before calling

if controller.scan_started or getattr(controller, '_start_in_progress', False):
    print('scan already starting/running; ignoring extra start')
else:
    await controller.handle("setup.start", {})

Type guard

def scan_is_idle(controller) -> bool:
    return not controller.scan_started and not getattr(controller, '_start_in_progress', False)

Try / catch

try:
    await controller.handle("setup.start", {})
except RuntimeError as exc:
    if 'already starting or running' in str(exc):
        pass  # benign duplicate start; first one is still in flight
    else:
        raise

Prevention

When it happens

Trigger: Double-pressing Enter/Start in the TUI before the first press finishes preparing; a frontend retry on slow ack sending setup.start again; racing the workspace-mount confirmation path which also flips scan_started.

Common situations: Slow model preflight making users click start twice; frontend event handlers not debounced; scripted clients firing start on a timer without checking state.

Related errors


AI-assisted analysis of usestrix/strix@8551339130 (2026-08-15). Data as JSON: /api/errors/8524b7ed864b5665. Report an issue: GitHub.