usestrix/strix · error · RuntimeError
Message could not be delivered
Error message
Message could not be delivered
What it means
Raised by _send_message after coordinator.send completed but returned a falsy 'delivered' result. The coordinator's send API returns a boolean indicating whether the target agent actually received the message; false means the agent id was accepted at the controller level but no live mailbox took the message (agent finished, was cancelled, or its inbox closed between the UI lookup and delivery).
Source
Thrown at strix/interface/tui/backend/controller.py:382
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:
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
)View on GitHub (pinned to 8551339130)
Solutions
- Refresh the agent list (live_view.agents) and resend to an agent with an active status
- Retry once — transient races during agent handoff usually clear
- Surface the failure to the UI so the user knows the message was not delivered
- If it persists for one agent id, that agent is gone; pick another agent or restart the scan
Example fix
# before
result = await controller._send_message({"agent_id": aid, "message": msg})
# after
try:
result = await controller._send_message({"agent_id": aid, "message": msg})
except RuntimeError:
# agent vanished mid-delivery; refresh and retry once with a live agent
agents = controller.live_view.agents
aid = next((a for a, m in agents.items() if m.get("status") == "running"), None)
if aid:
result = await controller._send_message({"agent_id": aid, "message": msg}) Defensive patterns
Strategy: retry
Validate before calling
agents = controller.live_view.agents
if agent_id not in agents or agents[agent_id].get("status") not in ("running", "waiting", "budget_paused"):
agent_id = pick_active_agent(agents) # refresh before sending Type guard
def agent_deliverable(agents: dict, aid: str) -> bool:
m = agents.get(aid)
return bool(m) and m.get("status") in {"running", "waiting", "budget_paused"} Try / catch
try:
await controller._send_message({"agent_id": aid, "message": msg})
except RuntimeError as e:
if "could not be delivered" in str(e):
aid = pick_active_agent(controller.live_view.agents)
if aid:
await controller._send_message({"agent_id": aid, "message": msg})
else:
raise Prevention
- Always pick the agent id from the latest live_view snapshot
- Retry once on delivery failure — races during agent handoff are transient
- Show delivery failures in the UI instead of silently dropping messages
When it happens
Trigger: Sending a message to an agent that completed or was stopped concurrently — the live_view still lists it, but coordinator.send returns False. Also occurs when the agent id is stale after a scan restart while old UI state persists.
Common situations: User types a message while the target agent is finishing its task; race between the agents panel render and the agent lifecycle; UI showing cached agent list after scan restart.
Related errors
- Scan is already starting or running
- Agent coordinator is unavailable
- Scan loop is not ready
- Agent '{agent_id}' is no longer active
- The interactive interface could not start: {exc}
AI-assisted analysis of usestrix/strix@8551339130 (2026-08-15).
Data as JSON: /api/errors/b962a6925a82c742.
Report an issue: GitHub.