Skip to content

fix: exit cleanly when stdout pipe closes early - #65

Draft
posthog[bot] wants to merge 2 commits into
mainfrom
posthog-self-driving/fixcli-exit-cleanly-on-closed-stdout-bcb995
Draft

posthog[bot] wants to merge 2 commits into
mainfrom
posthog-self-driving/fixcli-exit-cleanly-on-closed-stdout-bcb995

Conversation

@posthog

@posthog posthog Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Problem

  • Piping any print-heavy autter subcommand into a reader that closes early — autter blame | head is the everyday case — crashed the CLI with a panic instead of exiting quietly. The user saw a stack trace where they expected output, and each crash added noise to error tracking.
  • Root cause: Rust sets SIGPIPE to ignore before main, so when the pipe reader closes, the next write returns EPIPE and the plain println!/print! macro panics with failed printing to stdout: Broken pipe. The panic points at Rust's own library/std/src/io/stdio.rs, confirming the macro — not any one command — is at fault. Most subcommands print this way (blame, config, status, onboard, autter_handlers).

Changes

  • src/observability/mod.rs — the panic hook now recognizes the broken-pipe panic from the print macros and exits quietly (exit 0), without printing a panic or reporting telemetry. One central fix covers every println! site in every subcommand.
  • Why not reset SIGPIPE to SIG_DFL? That was the first attempt. autter is also its own daemon: the CLI (including the transparent git proxy) writes to the daemon control socket on nearly every invocation. Under SIG_DFL, a daemon that closed the socket mid-write would deliver SIGPIPE and silently kill the command, bypassing the existing reconnect/retry logic — a worse regression than the bug being fixed. Keeping SIGPIPE ignored (Rust's default) leaves every socket write's EPIPE handling intact and keeps the existing BrokenPipe handling in log.rs working as written.
  • Panic context — the running subcommand is now recorded and added to the panic report, which previously carried only a source location.

Test plan

  • New Unix integration test broken_pipe::blame_into_closed_pipe_does_not_panic: runs autter blame on a large file into a pipe whose reader closes after 64 bytes, then asserts the process exits cleanly (exit 0), with no panic on stderr.
  • Verified the test fails without the fix — reproducing the exact reported panic (failed printing to stdout: Broken pipe at library/std/src/io/stdio.rs) — and passes with it.
  • cargo build and cargo clippy clean for the changed code.

Agent context

  • First implemented the report's suggested SIG_DFL reset (plus a daemon SIG_IGN counter-measure); a review pass found the CLI-side control-socket writers were left exposed to a silent SIGPIPE kill, so the approach was changed to the central panic-hook interception above.
  • Note: log::log_plain_rejects_http_backend fails on main already (its expected message string does not match the code's) — pre-existing and unrelated to this change; left untouched.

Created with PostHog Desktop from this inbox report.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

posthog Bot added 2 commits September 21, 2026 18:41
Rust ignores SIGPIPE before `main`, so a reader that closes a pipe early
(e.g. `autter blame | head`) turned the closed pipe into an EPIPE that made
`println!` panic with "failed printing to stdout". Restore the Unix default
(SIG_DFL) at the top of `main` so the process exits quietly on a closed pipe,
like every other CLI tool. This removes the panic path for every print-heavy
subcommand at once.

The long-lived daemon re-ignores SIGPIPE (SIG_IGN) at startup so its network
and control-socket writes keep returning EPIPE instead of getting killed by a
signal.

Also record the current subcommand and add it to the panic report context,
which previously carried only a source location.

Generated-By: PostHog Desktop
Task-Id: 30cd172e-097b-4b6e-bce6-c8f73634b434
…g SIGPIPE

The first approach reset SIGPIPE to SIG_DFL process-wide. Review found that
the CLI (including the transparent git proxy) writes to the daemon control
socket on nearly every invocation; under SIG_DFL a daemon that closed the
socket mid-write would deliver SIGPIPE and silently kill the command,
bypassing the existing reconnect/retry logic. A daemon-only re-ignore did not
cover those CLI-side writers.

Keep SIGPIPE ignored (Rust's default, network-safe everywhere) and instead
recognize the broken-pipe panic from the print macros in the panic hook and
exit quietly (exit 0), without printing a panic or reporting telemetry. This
fixes the same symptom (`autter blame | head`) centrally, leaves every socket
write's EPIPE handling intact, and keeps the existing BrokenPipe handling in
log.rs working as written.

Also dedup: route the test helpers autter_with_env/autter_with_stdin through
the new autter_command, and compute the git subcommand once in handle_git.

Generated-By: PostHog Desktop
Task-Id: 30cd172e-097b-4b6e-bce6-c8f73634b434
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants