NousResearch/hermes-agent · error · TimeoutError

codex app-server method {method!r} timed out after {timeout}

Error message

codex app-server method {method!r} timed out after {timeout}s

What it means

TimeoutError from CodexAppServerClient.request() (agent/transports/codex_app_server.py:231). Each request registers a per-id response queue; if the codex app-server subprocess does not deliver a response for that id within `timeout` seconds, queue.get raises Empty, the pending entry is cleaned up, and this TimeoutError is raised naming the method and timeout.

Source

Thrown at agent/transports/codex_app_server.py:231

    def request(
        self,
        method: str,
        params: Optional[dict] = None,
        timeout: float = 30.0,
    ) -> dict:
        """Send a JSON-RPC request and block on the response. Returns `result`,
        raises CodexAppServerError on `error`."""
        rid = self._take_id()
        q: queue.Queue = queue.Queue(maxsize=1)
        with self._pending_lock:
            self._pending[rid] = _Pending(queue=q, method=method)
        self._send({"id": rid, "method": method, "params": params or {}})
        try:
            msg = q.get(timeout=timeout)
        except queue.Empty:
            with self._pending_lock:
                self._pending.pop(rid, None)
            raise TimeoutError(
                f"codex app-server method {method!r} timed out after {timeout}s"
            )
        if "error" in msg:
            err = msg["error"]
            raise CodexAppServerError(
                code=err.get("code", -1),
                message=err.get("message", ""),
                data=err.get("data"),
            )
        return msg.get("result", {})

    def notify(self, method: str, params: Optional[dict] = None) -> None:
        """Send a JSON-RPC notification (no id, no response expected)."""
        self._send({"method": method, "params": params or {}})

    def respond(self, request_id: Any, result: dict) -> None:
        """Reply to a server-initiated request (e.g. approval prompts)."""
        self._send({"id": request_id, "result": result})

View on GitHub (pinned to c896c09c42)

Solutions

  1. Pass a larger timeout for methods known to be slow: client.request('turn/start', params, timeout=120.0).
  2. Check the subprocess is alive and the reader thread is running (a dead process usually also produces stdin-closed errors from _send).
  3. If the process died, tear down the client and start a new one rather than retrying on the same connection.
  4. Retry idempotent methods once after a transient timeout, but never blind-retry initialize() on the same client (see the 'already initialized' error).

Example fix

# before
result = client.request("thread/start", params)  # default 10s

# after
result = client.request("thread/start", params, timeout=60.0)
Defensive patterns

Strategy: retry

Validate before calling

import subprocess

def codex_alive(proc: subprocess.Popen) -> bool:
    return proc.poll() is None and proc.stdin is not None and not proc.stdin.closed

Try / catch

try:
    result = client.request(method, params, timeout=timeout)
except TimeoutError:
    if client._proc.poll() is not None:
        raise  # child died; retrying is pointless
    result = client.request(method, params, timeout=timeout * 2)  # one bounded retry

Prevention

When it happens

Trigger: request(method, params, timeout=N) where the codex app-server is hung, still starting up, busy on a long operation (default timeout is 10.0s from initialize), or has died without the caller noticing; also a response routed to a dead reader thread never reaches the queue.

Common situations: Cold start of the codex binary exceeding the default 10s; a long-running turn/step method invoked with the default timeout; the app-server process crashed earlier and only the next request surfaces it; system under heavy load.

Understand the failure class

Related errors


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/d2eb519527ae2996. Report an issue: GitHub.