facebook/flow · error

failed to write cli errors

Error message

failed to write cli errors

What it means

For default human-readable output, `flow status` prints errors via flow_common_errors::error_utils::cli_output::print_errors to a locked stdout; this expect panics on the first write error. Projects with many errors generate many writes, so a downstream consumer that exits early (pager quit, `head`, `grep -m`) triggers BrokenPipe almost immediately, which Rust delivers as an Err because it ignores SIGPIPE.

Source

Thrown at rust_port/crates/flow_cli/src/status_command.rs:200

                .expect("failed to write vim/emacs errors");
                out.flush().expect("failed to flush vim/emacs errors");
            } else {
                let mut cli_errors = errors.clone();
                for (error, _) in &suppressed_errors {
                    cli_errors.add(error.clone());
                }
                let stdout = std::io::stdout();
                let mut out = stdout.lock();
                flow_common_errors::error_utils::cli_output::print_errors(
                    &mut out,
                    error_flags,
                    &None,
                    strip_root.as_deref(),
                    &cli_errors,
                    &warnings,
                    lazy_msg.as_deref(),
                )
                .expect("failed to write cli errors");
                out.flush().expect("failed to flush cli errors");
            }
            flow_common_exit_status::exit(command_utils::get_check_or_status_exit_code(
                &errors,
                &warnings,
                error_flags.max_warnings,
            ))
        }
        server_prot::response::StatusResponse::NO_ERRORS => {
            if args.output_json {
                print_json(
                    &ConcreteLocPrintableErrorSet::empty(),
                    &ConcreteLocPrintableErrorSet::empty(),
                    &[],
                )
            } else {
                println!("No errors!");
                if let Some(msg) = &lazy_msg {

View on GitHub (pinned to f88ac94bcf)

Solutions

  1. Write to a file and inspect that: `flow status > status.txt`.
  2. Keep the consumer reading until EOF (no head/grep -m in the pipeline).
  3. Free disk space when redirecting.
  4. Maintainer fix: BrokenPipe → exit(0); surface other io errors with a diagnostic instead of a raw panic.

Example fix

# before
flow status | head -20      # head exits, flow panics on BrokenPipe

# after
flow status > /tmp/flow-status.txt && head -20 /tmp/flow-status.txt
Defensive patterns

Strategy: try-catch

Try / catch

if let Err(e) = flow_common_errors::error_utils::cli_output::print_errors(&mut out, error_flags, &None, strip_root.as_deref(), &cli_errors, &warnings, lazy_msg.as_deref()) {
    if e.kind() == std::io::ErrorKind::BrokenPipe {
        std::process::exit(0);
    }
    panic!("failed to write cli errors: {}", e);
}

Prevention

When it happens

Trigger: `flow status | head -20` on an error-heavy project; piping into less and pressing 'q'; consumer crashing mid-stream; stdout redirected to a full disk; stdout closed with `>&-`.

Common situations: Developers paginating or truncating large status output; CI log pipelines with early-terminating stages; disk-full CI runners.

Related errors


AI-assisted analysis of facebook/flow@f88ac94bcf (2026-08-20). Data as JSON: /api/errors/f978dbf197ffcf24. Report an issue: GitHub.