openclaw - ✅(Solved) Fix Gateway crashes with unhandled write EPIPE when CodexAppServerClient receives unparseable stdout [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#67886Fetched 2026-04-17 08:29:06
View on GitHub
Comments
0
Participants
1
Timeline
3
Reactions
0
Participants
Timeline (top)
cross-referenced ×2referenced ×1

The CodexAppServerClient communicates with the codex-acp subprocess over stdio. If the subprocess encounters an initialization failure (for instance, an interactive prompt emitted to stdout, or a deprecated config file throwing an error string), the subprocess writes plaintext text to stdout and then abruptly exits.

Currently, CodexAppServerClient.handleLine attempts to JSON.parse() this stdout text, fails, and catches the parsing error. When the child process exits immediately after (closing its pipe), OpenClaw subsequently attempts to write the JSON-RPC initialize payload to the deceased subprocess's stdin. Because no error listener is attached to the stdin stream, Node.js throws an unhandled write EPIPE exception, which cascades to immediately terminate the entire OpenClaw gateway daemon.

Error Message

The CodexAppServerClient communicates with the codex-acp subprocess over stdio. If the subprocess encounters an initialization failure (for instance, an interactive prompt emitted to stdout, or a deprecated config file throwing an error string), the subprocess writes plaintext text to stdout and then abruptly exits. Currently, CodexAppServerClient.handleLine attempts to JSON.parse() this stdout text, fails, and catches the parsing error. When the child process exits immediately after (closing its pipe), OpenClaw subsequently attempts to write the JSON-RPC initialize payload to the deceased subprocess's stdin. Because no error listener is attached to the stdin stream, Node.js throws an unhandled write EPIPE exception, which cascades to immediately terminate the entire OpenClaw gateway daemon. Attach an error event handler to child.stdin to swallow expected pipe lifecycle tear-down errors: child.stdin.on('error', (err) => { log.debug('codex app-server stdin error', { err });

Root Cause

Currently, CodexAppServerClient.handleLine attempts to JSON.parse() this stdout text, fails, and catches the parsing error. When the child process exits immediately after (closing its pipe), OpenClaw subsequently attempts to write the JSON-RPC initialize payload to the deceased subprocess's stdin. Because no error listener is attached to the stdin stream, Node.js throws an unhandled write EPIPE exception, which cascades to immediately terminate the entire OpenClaw gateway daemon.

Fix Action

Fixed

PR fix notes

PR #67945: fix(codex): attach stdin error listener to prevent gateway EPIPE crash

Description (problem / solution / changelog)

Summary

Fixes #67886 — attaches a stdin error listener to CodexAppServerClient so the codex app-server subprocess can fail cleanly without taking down the whole OpenClaw gateway daemon.

Root cause

CodexAppServerTransport.stdin is a Node Writable stream. 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 asynchronous error event on that stream; with no listener attached, the default handler rethrows, crashing the entire gateway daemon.

The existing constructor already wires stdout / stderr / error / exit handlers on the child, but never wires anything on stdin. This PR adds a debug-level listener so the pipe teardown noise is absorbed while the existing exit handler continues to run closeWithError.

Changes

  • extensions/codex/src/app-server/client.ts — attach child.stdin.on('error', …) in the constructor, logging at debug level.
  • extensions/codex/src/app-server/transport.ts — widen the typed stdin shape to expose an optional on('error', …) method so tests and alternate transports participate without casting.
  • extensions/codex/src/app-server/client.test.ts — assert that emitting EPIPE on 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.
  • Added unit test 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.ts fails to resolve base TestRunner import) — reproduces identically on main, so it is not caused by this change. CI should exercise the new test normally.

Changed files

  • extensions/codex/src/app-server/client.test.ts (modified, +8/-0)
  • extensions/codex/src/app-server/client.ts (modified, +3/-0)
  • extensions/codex/src/app-server/transport.ts (modified, +1/-0)

Code Example

if (child.stdin) {
    child.stdin.on('error', (err) => {
        if (err.code !== 'EPIPE') {
            log.debug('codex app-server stdin error', { err });
        }
    });
}
RAW_BUFFERClick to expand / collapse

Environment

  • Version: 2026.4.15
  • OS: Linux
  • Install Method: script

Description

The CodexAppServerClient communicates with the codex-acp subprocess over stdio. If the subprocess encounters an initialization failure (for instance, an interactive prompt emitted to stdout, or a deprecated config file throwing an error string), the subprocess writes plaintext text to stdout and then abruptly exits.

Currently, CodexAppServerClient.handleLine attempts to JSON.parse() this stdout text, fails, and catches the parsing error. When the child process exits immediately after (closing its pipe), OpenClaw subsequently attempts to write the JSON-RPC initialize payload to the deceased subprocess's stdin. Because no error listener is attached to the stdin stream, Node.js throws an unhandled write EPIPE exception, which cascades to immediately terminate the entire OpenClaw gateway daemon.

Expected Behavior

A misconfigured or failing child plugin subprocess should fail gracefully without taking down the entire OpenClaw gateway daemon.

Proposed Fix

Attach an error event handler to child.stdin to swallow expected pipe lifecycle tear-down errors:

if (child.stdin) {
    child.stdin.on('error', (err) => {
        if (err.code !== 'EPIPE') {
            log.debug('codex app-server stdin error', { err });
        }
    });
}

extent analysis

TL;DR

Attach an error event handler to child.stdin to prevent the OpenClaw gateway daemon from terminating due to unhandled write EPIPE exceptions.

Guidance

  • Verify that the child.stdin stream is properly set up and accessible before attempting to attach an event handler.
  • Implement the proposed fix by attaching an error event handler to child.stdin as shown in the provided code snippet.
  • Ensure that the error handler correctly swallows expected EPIPE errors while logging other unexpected errors for debugging purposes.
  • Test the fix by simulating a subprocess initialization failure and verifying that the OpenClaw gateway daemon remains operational.

Example

The provided code snippet demonstrates how to attach an error event handler to child.stdin:

if (child.stdin) {
    child.stdin.on('error', (err) => {
        if (err.code !== 'EPIPE') {
            log.debug('codex app-server stdin error', { err });
        }
    });
}

This code checks if child.stdin is truthy before attaching the event handler, ensuring that the handler is only added when the stream is available.

Notes

This fix assumes that the child.stdin stream is properly set up and that the error event handler is correctly implemented. If issues persist, further debugging may be necessary to identify the root cause.

Recommendation

Apply the proposed workaround by attaching an error event handler to child.stdin, as this should prevent the OpenClaw gateway daemon from terminating due to unhandled write EPIPE exceptions.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING