t8y2/dbx · error · RuntimeError

agent exited before {method}

Error message

agent exited before {method}

What it means

call() refuses to send a JSON-RPC request when the agent subprocess has already terminated, raising RuntimeError with the stderr tail. This prevents hangs or broken pipes when the child process died between calls. It is raised eagerly at the start of call(), before any request is written.

Source

Thrown at agents/drivers/hive-go/bench/agent_compare.py:80

            with self.request_lock:
                response_queue = self.pending.get(response_id)
            if response_queue is not None:
                response_queue.put(response)
        self.exited.set()
        self.ready.set()
        with self.request_lock:
            pending = list(self.pending.values())
        for response_queue in pending:
            response_queue.put(RuntimeError(self._failure("agent process exited")))

    def _drain_stderr(self) -> None:
        assert self.process.stderr is not None
        for line in self.process.stderr:
            self.stderr_lines.append(line.rstrip())

    def call(self, method: str, params: dict | None = None) -> object:
        if self.exited.is_set():
            raise RuntimeError(self._failure(f"agent exited before {method}"))
        with self.request_lock:
            self.request_id += 1
            request_id = self.request_id
            response_queue: queue.Queue = queue.Queue(maxsize=1)
            self.pending[request_id] = response_queue
        request = {
            "jsonrpc": "2.0",
            "id": request_id,
            "method": method,
            "params": params or {},
        }
        try:
            assert self.process.stdin is not None
            with self.write_lock:
                self.process.stdin.write(json.dumps(request, separators=(",", ":")) + "\n")
                self.process.stdin.flush()
            response = response_queue.get(timeout=env_float("BENCH_RPC_TIMEOUT", 180.0))
        except queue.Empty as error:

View on GitHub (pinned to c0390bff16)

Solutions

  1. Inspect the stderr tail in the error message and any earlier log lines to find why the agent exited (OOM, panic, unhandled exception)
  2. Check dmesg / container logs for OOM kills and raise the memory limit
  3. Rerun with a smaller workload to isolate the crashing operation
  4. Update/fix the agent so the failing method returns an RPC error instead of dying
Defensive patterns

Strategy: try-catch

Validate before calling

def ensure_alive(process) -> None:
    if process.exited.is_set():
        raise SystemExit(
            "agent already exited: " + "\n".join(process.stderr_lines[-20:])
        )
ensure_alive(process)  # before issuing RPCs

Type guard

def agent_alive(process) -> bool:
    return not process.exited.is_set() and process.poll() is None

Try / catch

try:
    result = process.call("execute_query", params)
except RuntimeError as e:
    if "agent exited before" in str(e):
        print(e)  # stderr tail shows crash cause
        process = restart_agent()
        result = process.call("execute_query", params)
    else:
        raise

Prevention

When it happens

Trigger: Invoking any RPC (execute_query, execute_query_page, close, etc.) after the agent process exited — due to a crash on a previous call, OOM-kill of the subprocess, or an explicit terminate from an earlier failure path.

Common situations: Agent killed by OOM killer during a memory-heavy benchmark; agent segfaulted on malformed input; harness bug double-closing a session; running the bench in a container with a low memory limit where the child is reaped.

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/f09ae7559e771204. Report an issue: GitHub.