{"record":{"id":"d2eb519527ae2996","repo":"NousResearch/hermes-agent","slug":"codex-app-server-method-method-r-timed-out-after","errorCode":null,"errorMessage":"codex app-server method {method!r} timed out after {timeout}s","messagePattern":"codex app-server method (.+?) timed out after (.+?)s","errorType":"exception","errorClass":"TimeoutError","httpStatus":null,"severity":"error","filePath":"agent/transports/codex_app_server.py","lineNumber":231,"sourceCode":"    def request(\n        self,\n        method: str,\n        params: Optional[dict] = None,\n        timeout: float = 30.0,\n    ) -> dict:\n        \"\"\"Send a JSON-RPC request and block on the response. Returns `result`,\n        raises CodexAppServerError on `error`.\"\"\"\n        rid = self._take_id()\n        q: queue.Queue = queue.Queue(maxsize=1)\n        with self._pending_lock:\n            self._pending[rid] = _Pending(queue=q, method=method)\n        self._send({\"id\": rid, \"method\": method, \"params\": params or {}})\n        try:\n            msg = q.get(timeout=timeout)\n        except queue.Empty:\n            with self._pending_lock:\n                self._pending.pop(rid, None)\n            raise TimeoutError(\n                f\"codex app-server method {method!r} timed out after {timeout}s\"\n            )\n        if \"error\" in msg:\n            err = msg[\"error\"]\n            raise CodexAppServerError(\n                code=err.get(\"code\", -1),\n                message=err.get(\"message\", \"\"),\n                data=err.get(\"data\"),\n            )\n        return msg.get(\"result\", {})\n\n    def notify(self, method: str, params: Optional[dict] = None) -> None:\n        \"\"\"Send a JSON-RPC notification (no id, no response expected).\"\"\"\n        self._send({\"method\": method, \"params\": params or {}})\n\n    def respond(self, request_id: Any, result: dict) -> None:\n        \"\"\"Reply to a server-initiated request (e.g. approval prompts).\"\"\"\n        self._send({\"id\": request_id, \"result\": result})","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/transports/codex_app_server.py#L213-L249","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass a larger timeout for methods known to be slow: client.request('turn/start', params, timeout=120.0).","Check the subprocess is alive and the reader thread is running (a dead process usually also produces stdin-closed errors from _send).","If the process died, tear down the client and start a new one rather than retrying on the same connection.","Retry idempotent methods once after a transient timeout, but never blind-retry initialize() on the same client (see the 'already initialized' error)."],"exampleFix":"# before\nresult = client.request(\"thread/start\", params)  # default 10s\n\n# after\nresult = client.request(\"thread/start\", params, timeout=60.0)","handlingStrategy":"retry","validationCode":"import subprocess\n\ndef codex_alive(proc: subprocess.Popen) -> bool:\n    return proc.poll() is None and proc.stdin is not None and not proc.stdin.closed","typeGuard":null,"tryCatchPattern":"try:\n    result = client.request(method, params, timeout=timeout)\nexcept TimeoutError:\n    if client._proc.poll() is not None:\n        raise  # child died; retrying is pointless\n    result = client.request(method, params, timeout=timeout * 2)  # one bounded retry","preventionTips":["Pass explicit, per-method timeouts sized to the operation (initialize default is 10s).","Watch the reader thread / process liveness so a dead server is detected before a request times out.","Only retry idempotent methods; never blind-retry the initialize handshake on the same client."],"tags":["jsonrpc","codex","timeout","subprocess"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}