facebook/flow · error

failed to write cli errors

Error message

failed to write cli errors

What it means

Non-JSON path of check-contents: when the server returns ERRORS and --json is not set, cli_output::print_errors renders human-readable diagnostics (with strip-root and offset-kind adjustments) to locked stdout. The .expect() panics on the first failing write. With large error reports this is the classic EPIPE crash: Rust disables SIGPIPE, so `| head` closing the pipe early turns the next formatted write into an Err.

Source

Thrown at rust_port/crates/flow_cli/src/check_contents_command.rs:168

            errors,
            warnings,
            suppressed_errors,
        } => {
            if json {
                print_json(&errors, &warnings, &suppressed_errors)
            } else {
                let stdout = std::io::stdout();
                let mut out = stdout.lock();
                flow_common_errors::error_utils::cli_output::print_errors(
                    &mut out,
                    &error_flags,
                    &stdin_file,
                    strip_root.as_deref(),
                    &errors,
                    &warnings,
                    None,
                )
                .expect("failed to write cli errors");
                out.flush().expect("failed to flush cli errors");
                // Return a successful exit code if there were only warnings.
                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 json {
                print_json(
                    &ConcreteLocPrintableErrorSet::empty(),
                    &ConcreteLocPrintableErrorSet::empty(),
                    &[],
                )
            } else {
                let stdout = std::io::stdout();

View on GitHub (pinned to f88ac94bcf)

Solutions

  1. Buffer with cat (`flow check-contents file.js | cat | head -50`) or page a saved file (`flow check-contents file.js > errors.txt; less errors.txt`)
  2. Take the full output and filter it afterwards instead of streaming into an early-exiting reader
  3. Free space / fix the redirect target if writing to a file or device
  4. Maintainer: match the Result and exit(141) on BrokenPipe instead of expect

Example fix

// before
flow_common_errors::error_utils::cli_output::print_errors(&mut out, /* ... */)
    .expect("failed to write cli errors");

// after
if let Err(e) = flow_common_errors::error_utils::cli_output::print_errors(&mut out, /* ... */) {
    if e.kind() == std::io::ErrorKind::BrokenPipe {
        std::process::exit(141);
    }
    panic!("failed to write cli errors: {e}");
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Spawn wrapper: page a SAVED report, never the live stream:
// flow check-contents file.js > errors.txt && less errors.txt

Try / catch

if let Err(e) = cli_output::print_errors(&mut out, /* ... */) {
    if e.kind() == std::io::ErrorKind::BrokenPipe {
        std::process::exit(141);
    }
    panic!("failed to write cli errors: {e}");
}

Prevention

When it happens

Trigger: `flow check-contents file.js | head -50` on a file producing hundreds of errors; piping the report into `less` and quitting with `q` while output still streams; stdout redirected to a full disk.

Common situations: Developers paging through large error reports and quitting early; CI scripts sampling the first N lines of diagnostics; output redirected to a nearly-full volume.

Related errors


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