{"record":{"id":"f48b91b3f1c15c7f","repo":"NousResearch/hermes-agent","slug":"codex-app-server-stdin-not-available","errorCode":null,"errorMessage":"codex app-server stdin not available","messagePattern":"codex app-server stdin not available","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"agent/transports/codex_app_server.py","lineNumber":305,"sourceCode":"\n    def is_alive(self) -> bool:\n        return self._proc.poll() is None\n\n    # ---------- internals ----------\n\n    def _take_id(self) -> int:\n        # JSON-RPC ids only need to be unique per-connection. A simple\n        # monotonically increasing int is the common choice and matches what\n        # codex's own clients use.\n        rid = self._next_id\n        self._next_id += 1\n        return rid\n\n    def _send(self, obj: dict) -> None:\n        if self._closed:\n            raise RuntimeError(\"codex app-server client is closed\")\n        if self._proc.stdin is None:\n            raise RuntimeError(\"codex app-server stdin not available\")\n        try:\n            self._proc.stdin.write((json.dumps(obj) + \"\\n\").encode(\"utf-8\"))\n            self._proc.stdin.flush()\n        except (BrokenPipeError, ValueError) as exc:\n            raise RuntimeError(\n                f\"codex app-server stdin closed unexpectedly: {exc}\"\n            ) from exc\n\n    def _read_stdout(self) -> None:\n        if self._proc.stdout is None:\n            return\n        try:\n            for line in iter(self._proc.stdout.readline, b\"\"):\n                if not line:\n                    break\n                line = line.strip()\n                if not line:\n                    continue","sourceCodeStart":287,"sourceCodeEnd":323,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/transports/codex_app_server.py#L287-L323","documentation":"RuntimeError('codex app-server stdin not available') from _send() (agent/transports/codex_app_server.py:305). It fires when self._proc.stdin is None, i.e. the subprocess was spawned without a stdin pipe, so there is no channel to write JSON-RPC frames to. This is a spawn-configuration bug in the caller, not a runtime failure of codex.","triggerScenarios":"The Popen call that created the codex app-server process lacked stdin=PIPE (or explicitly set stdin=DEVNULL/None), then any request/notify hits the guard immediately.","commonSituations":"A custom spawn path (test harness, alternate launcher) copies a generic subprocess helper that only pipes stdout/stderr; refactoring the spawn code drops the stdin pipe; Windows-specific spawn flags accidentally overriding stdin.","solutions":["Spawn the app-server with subprocess.Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=...) so the JSON-RPC framing over stdio works.","Fail fast: validate _proc.stdin is not None right after spawn instead of on first send.","Reuse the client's own spawn helper rather than constructing the process manually."],"exampleFix":"# before\nproc = subprocess.Popen(cmd, stdout=subprocess.PIPE)  # no stdin pipe\n\n# after\nproc = subprocess.Popen(\n    cmd,\n    stdin=subprocess.PIPE,\n    stdout=subprocess.PIPE,\n    stderr=subprocess.PIPE,\n)","handlingStrategy":"validation","validationCode":"import subprocess\n\ndef spawn_app_server(cmd: list[str]) -> subprocess.Popen:\n    proc = subprocess.Popen(cmd, stdin=subprocess.PIPE,\n                            stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n    assert proc.stdin is not None, \"spawn must pipe stdin for JSON-RPC\"\n    return proc","typeGuard":null,"tryCatchPattern":"try:\n    client.request(\"initialize\", {})\nexcept RuntimeError as exc:\n    if \"stdin not available\" in str(exc):\n        raise RuntimeError(\"respawn the app-server with stdin=PIPE\") from exc\n    raise","preventionTips":["Always spawn stdio JSON-RPC servers with stdin=PIPE and stdout=PIPE.","Validate both pipes immediately after Popen, not on first use.","Prefer the client's own spawn helper over manual subprocess construction."],"tags":["subprocess","codex","jsonrpc","spawn-config"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}