usestrix/strix · error · ValueError

Unknown agent: {agent_id}

Error message

Unknown agent: {agent_id}

What it means

Raised by _stop_agent when the supplied agent_id does not exist in live_view.agents, the controller's live snapshot of agents in the current scan. It is a ValueError (bad input) rather than a state error: the id itself is wrong before any stop machinery is consulted. Ids are exact strings, so any mismatch in casing, prefix, or staleness triggers it.

Source

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

            )
        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:
            raise RuntimeError("Message could not be delivered")
        return {"sent": True}

    async def _stop_agent(self, payload: dict[str, Any]) -> dict[str, Any]:
        agent_id = self._required_string(payload, "agent_id")
        agent = self.live_view.agents.get(agent_id)
        if agent is None:
            raise ValueError(f"Unknown agent: {agent_id}")
        status = str(agent.get("status", ""))
        if status not in _STOPPABLE_AGENT_STATUSES:
            raise RuntimeError(f"Agent '{agent_id}' cannot be stopped while {status or 'unknown'}")
        if self.coordinator is None or self.scan_loop is None or self.scan_loop.is_closed():
            raise RuntimeError("Scan loop is not ready")
        if self.scan_loop is asyncio.get_running_loop():
            accepted = await self.coordinator.cancel_descendants_graceful(agent_id)
        else:
            future = asyncio.run_coroutine_threadsafe(
                self.coordinator.cancel_descendants_graceful(agent_id), self.scan_loop
            )
            accepted = await asyncio.wrap_future(future)
        if not accepted:
            raise RuntimeError(f"Agent '{agent_id}' is no longer active")
        return {"stopped": True}

    async def _open_viewer(self, _payload: dict[str, Any]) -> dict[str, Any]:
        if self.viewer_url:

View on GitHub (pinned to 8551339130)

Solutions

  1. Re-read controller.live_view.agents and use an id that currently exists
  2. Print the available agent ids on failure so the caller can self-correct
  3. In scripts, always discover ids from the live view at call time instead of hardcoding
  4. Verify the id matches exactly (case-sensitive, full string)

Example fix

# before
await controller._stop_agent({"agent_id": "scanner"})

# after
agents = controller.live_view.agents
if "scanner" not in agents:
    raise SystemExit(f"unknown agent; available: {sorted(agents)}")
await controller._stop_agent({"agent_id": "scanner"})
Defensive patterns

Strategy: validation

Validate before calling

if agent_id not in controller.live_view.agents:
    raise KeyError(f"unknown agent {agent_id}; known: {sorted(controller.live_view.agents)}")

Type guard

def is_known_agent(agents: dict, aid: str) -> bool:
    return aid in agents

Try / catch

try:
    await controller._stop_agent({"agent_id": aid})
except ValueError as e:
    if str(e).startswith("Unknown agent"):
        aid = pick_from(controller.live_view.agents)  # refresh id
    else:
        raise

Prevention

When it happens

Trigger: Calling stop-agent with an id copied from a previous scan, a truncated id, or an id assembled by the frontend (e.g. missing a numeric suffix). The lookup is live_view.agents.get(agent_id) returning None.

Common situations: UI list is stale after agents spawned or finished; user pastes an agent id from an older run; automated scripts iterating a cached agent list; typo in hand-written ids.

Related errors


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