fix(codex): attach stdin error listener to prevent gateway EPIPE crash#67945
fix(codex): attach stdin error listener to prevent gateway EPIPE crash#67945hclsys wants to merge 1 commit into
Conversation
Wire a no-op 'error' listener on the codex app-server subprocess stdin. When the child exits after writing unparseable text (init failures, deprecated config errors) and OpenClaw subsequently writes to the already-closed pipe, Node emits an asynchronous 'error' event. Without a listener the default behavior is to throw, which was crashing the entire gateway daemon instead of letting the existing exit/error handlers clean up gracefully. Also widen the transport `stdin` type to expose an optional `on` method so tests and alternate transports can participate without casting. Fixes openclaw#67886
Greptile SummaryAttaches an Confidence Score: 5/5Safe to merge — minimal, targeted fix with correct logic and a direct regression test. The change is small and focused: one 3-line listener attachment, one optional type widening, and one directly validating test. The error handler does not alter the existing exit / closeWithError flow; EPIPE is purely absorbed at debug level, which is the right behavior for pipe-teardown noise. No P0/P1 findings were identified. No files require special attention. Reviews (1): Last reviewed commit: "fix(codex): attach stdin error listener ..." | Re-trigger Greptile |
Summary
Fixes #67886 — attaches a stdin error listener to
CodexAppServerClientso the codex app-server subprocess can fail cleanly without taking down the whole OpenClaw gateway daemon.Root cause
CodexAppServerTransport.stdinis a NodeWritablestream. When the subprocess exits after emitting unparseable stdout (for example an interactive prompt on init, or a deprecated-config error), OpenClaw still tries to write the JSON-RPC initialize payload to the now-closed pipe. Node emits an asynchronouserrorevent on that stream; with no listener attached, the default handler rethrows, crashing the entire gateway daemon.The existing constructor already wires
stdout/stderr/error/exithandlers on the child, but never wires anything onstdin. This PR adds a debug-level listener so the pipe teardown noise is absorbed while the existingexithandler continues to runcloseWithError.Changes
extensions/codex/src/app-server/client.ts— attachchild.stdin.on('error', …)in the constructor, logging at debug level.extensions/codex/src/app-server/transport.ts— widen the typedstdinshape to expose an optionalon('error', …)method so tests and alternate transports participate without casting.extensions/codex/src/app-server/client.test.ts— assert that emittingEPIPEon the harness stdin does not throw.Test plan
NODE_OPTIONS=--max-old-space-size=8192 npx tsc --noEmit— no new TS errors on touched files (baseline 243 on main, 243 on branch).pnpm exec oxlint <touched files>— 0 warnings, 0 errors.swallows EPIPE errors emitted on stdin after subprocess exit.Local full vitest run is blocked by pre-existing infra drift on my machine (
test/non-isolated-runner.tsfails to resolve baseTestRunnerimport) — reproduces identically onmain, so it is not caused by this change. CI should exercise the new test normally.