Skip to content

Commit b9018c4

Browse files
committed
fix: exit with code 1 on EIO, not 0
EPIPE and EIO have different semantics: EPIPE means the consumer chose to stop reading (exit 0), but EIO means output was lost/incomplete due to an I/O failure, which should signal failure to callers in scripts and CI pipelines.
1 parent b478e4e commit b9018c4

1 file changed

Lines changed: 9 additions & 5 deletions

File tree

src/bin.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,22 @@
66
* the full CLI with telemetry, middleware, and error recovery.
77
*/
88

9-
// Exit cleanly on non-recoverable stream I/O errors.
9+
// Handle non-recoverable stream I/O errors gracefully instead of crashing.
1010
// - EPIPE (errno -32): downstream pipe consumer closed (e.g., `sentry issue list | head`).
11-
// Normal Unix behavior — not an error. Node.js/Bun ignore SIGPIPE at the process level,
12-
// so pipe write failures surface as async 'error' events on the stream.
11+
// Normal Unix behavior — not an error. Exit 0 because the CLI succeeded; the consumer
12+
// just stopped reading.
1313
// - EIO (errno -5): low-level I/O failure on the stream fd (e.g., terminal device driver
1414
// error, broken PTY, disk I/O failure on redirected output). Non-recoverable — the
15-
// stream is unusable. Seen in CLI-H2 on self-hosted macOS with virtualized storage.
15+
// stream is unusable and output may be incomplete. Exit 1 so callers (scripts, CI) know
16+
// the output was lost. Seen in CLI-H2 on self-hosted macOS with virtualized storage.
1617
// Without this handler these errors become uncaught exceptions.
1718
function handleStreamError(err: NodeJS.ErrnoException): void {
18-
if (err.code === "EPIPE" || err.code === "EIO") {
19+
if (err.code === "EPIPE") {
1920
process.exit(0);
2021
}
22+
if (err.code === "EIO") {
23+
process.exit(1);
24+
}
2125
throw err;
2226
}
2327

0 commit comments

Comments
 (0)