usestrix/strix · error · RuntimeError

Agent coordinator is unavailable

Error message

Agent coordinator is unavailable

What it means

Raised by _send_message when the controller's coordinator reference is None, meaning no agent coordinator has been constructed yet. The coordinator is created during scan startup (_begin_scan), so this error means a chat message was routed to an agent before any scan exists. It is a state error, not a network error: the handler checks the invariant before attempting any delivery.

Source

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

        mount = self.pending_workspace_mount
        if mount is None:
            raise RuntimeError("No mount confirmation is pending")
        approved = payload.get("approved")
        if not isinstance(approved, bool):
            raise TypeError("approved must be a boolean")
        self.pending_workspace_mount = None
        # Declining skips the mount, it does not abandon the scan. The prompt is
        # the whole of the input either way; the working directory is only an
        # extra the agent may look at, so the run goes ahead without one.
        self.workspace_mount = mount if approved else None
        await self._begin_scan(self._pending_verify)
        return {"approved": approved}

    async def _send_message(self, payload: dict[str, Any]) -> dict[str, Any]:
        agent_id = self._required_string(payload, "agent_id")
        message = self._required_string(payload, "message")
        if self.coordinator is None:
            raise RuntimeError("Agent coordinator is unavailable")
        if self.scan_loop is None or self.scan_loop.is_closed():
            raise RuntimeError("Scan loop is not ready")
        self.live_view.record_user_message(agent_id, message)
        if self.scan_loop is asyncio.get_running_loop():
            delivered = await self.coordinator.send(
                agent_id,
                {"from": "user", "content": message, "type": "instruction"},
            )
        else:
            future = asyncio.run_coroutine_threadsafe(
                self.coordinator.send(
                    agent_id,
                    {"from": "user", "content": message, "type": "instruction"},
                ),
                self.scan_loop,
            )
            delivered = await asyncio.wrap_future(future)
        if not delivered:

View on GitHub (pinned to 8551339130)

Solutions

  1. Only enable the message input after the scan has started (scan_state == 'running' and coordinator exists)
  2. If the message was sent during startup, retry once the scan is live
  3. Guard the UI action on controller.scan_started / scan_state before dispatching send-message
  4. In tests, start a scan (or inject a fake coordinator) before exercising _send_message

Example fix

// before
await controller._send_message({"agent_id": "planner", "message": "hi"})

// after
if controller.scan_started:
    await controller._send_message({"agent_id": "planner", "message": "hi"})
Defensive patterns

Strategy: validation

Validate before calling

if controller.coordinator is None or not controller.scan_started:
    show_status("Start a scan before messaging agents")
    return

Type guard

def scan_ready(c) -> bool:
    return c.coordinator is not None and c.scan_started and c.scan_state == "running"

Try / catch

try:
    await controller._send_message(payload)
except RuntimeError as e:
    if "coordinator is unavailable" in str(e):
        disable_chat_input()  # scan not started yet
    else:
        raise

Prevention

When it happens

Trigger: Invoking the send-message command with valid agent_id/message strings before a scan has started (coordinator is None), or after the controller was reset and the coordinator torn down. The agent_id and message fields already passed _required_string validation when this fires.

Common situations: Frontend fires a chat send during the setup screen; a queued message is dispatched after scan teardown; race between the UI enabling the chat input and the backend creating the coordinator; tests instantiating the controller without starting a scan.

Related errors


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