fix(cli): exit cleanly instead of crashing on EPIPE#3038
Merged
Conversation
Piping quicktype's output into a program that closes the pipe early (e.g. `... | head -n 4`) left an unhandled 'error' event on process.stdout/stderr. Node treated the resulting EPIPE as an uncaught exception, printing a raw stack trace and exiting with code 1 instead of exiting quietly like well-behaved Unix CLIs do. Add EPIPE error handlers on process.stdout/stderr in the CLI entry point that exit(0) on EPIPE and rethrow any other stream error. Co-Authored-By: gpt-5.6-sol via pi <noreply@openai.com>
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.
Bug
Piping quicktype's output into a program that closes the pipe early (e.g.
quicktype ... | head -n 4) caused quicktype to crash with an unhandled'error'event instead of exiting quietly, as well-behaved Unix CLIs do (comparecat foo | head -n 1, which exits 0 silently).Repro:
Before the fix, stderr contained a raw Node.js stack trace:
and the quicktype process exited with code
1.Root cause
writeOutput()insrc/index.tswrites generated code viaprocess.stdout.write(...). When the downstream reader (e.g.head) closes its end of the pipe, subsequent writes fail withEPIPE. Node emits this as an'error'event on theprocess.stdout/process.stderrwritable streams; since nothing listened for it, Node treated it as an uncaught exception, printing a stack trace and exiting with code 1. The top-levelmain(...).catch(...)handler only catches promise rejections frommain(), not this asynchronous stream error event.Fix
Register
'error'listeners onprocess.stdoutandprocess.stderrin the CLI entry point (only when running as the CLI, i.e. insideif (require.main === module)) that exit(0) cleanly when the error isEPIPE, and rethrow any other stream error so real failures are still surfaced.Test coverage
This is CLI/process-level behavior (an unhandled stream error causing an uncaught-exception crash), not generated-output content, so it isn't expressible through the JSON/JSON-Schema fixture harness which asserts on generated code. Added a unit test,
test/unit/cli-epipe.test.ts, that spawns the built CLI (dist/index.js) againsttest/inputs/schema/vega-lite.schema, destroys its stdout pipe early (simulating| head), and asserts the process exits with code 0 and that stderr contains no EPIPE stack trace / "Unhandled 'error' event" text.expected 1 to be 0).Verification performed locally
npm run build— passes.npx vitest run test/unit— 27 files / 171 tests, all passing (including the new test).Fixes #864
🤖 Generated with Claude Code