fix(engines): swallow EPIPE on subprocess stdin#13
Merged
Conversation
When a CLI subprocess exits before the parent finishes writing the prompt (e.g. argument validation fails on launch), Node emits an EPIPE error event on child.stdin. Without a listener, that error bubbles as an uncaught exception and crashes whatever process owns the engine — Vitest in CI, the gateway in production, or the agent loop interactively. The exit code path that already runs after the subprocess closes is the source of truth for failure reporting, so silently dropping EPIPE on the stdin stream is safe. Other error codes are surfaced as a text_delta so they don't disappear silently. Surfaced as an uncaught error in the subprocess.test.ts run on PR #12 CI: all 2711 tests passed but the runner exited 1.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
errorlistener tochild.stdininSubprocessEngineso EPIPE doesn't crash the host process when a subprocess exits before the parent finishes writing.text_deltaso they don't disappear silently. The exit code path remains the source of truth for failure reporting.Why
Surfaced as a CI failure on PR #12: all 2711 tests passed, but the runner exited 1 because an uncaught EPIPE bubbled out of
subprocess.ts:310after the test had already completed. Same bug affects the gateway and interactive agent loop in production — any CLI subprocess that dies during launch would take the host process with it.CI run: https://github.com/ch4p-labs/ch4p/actions/runs/24276136757/job/70890302469
Test plan
npx vitest run packages/engines/src/subprocess.test.ts— 71/71 pass, no uncaught errorsnpx vitest run— 2711/2711 pass, exit code 0corepack pnpm -r build— cleancorepack pnpm audit— 0 vulnerabilities