facebook/flow · error

failed to write json errors

Error message

failed to write json errors

What it means

In flow's check-contents command, once the flow server replies with a StatusResponse::ERRORS payload and JSON output is active, print_errors_with_offset_kind serializes errors, warnings, and suppressed errors to locked stdout and returns io::Result. The .expect() converts any write failure into a panic, aborting before the proper exit code can be reported. The dominant cause is EPIPE: Rust ignores SIGPIPE, so a downstream pipe reader (head, grep -m1, grep -q) that already exited surfaces as a write error here instead of killing the process silently.

Source

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

         suppressed_errors: &[(PrintableError<Loc>, BTreeSet<Loc>)]| {
            let strip_root = strip_root
                .as_deref()
                .map(|root| root.to_string_lossy().into_owned());
            let stdout = std::io::stdout();
            let mut out = stdout.lock();
            flow_common_errors::error_utils::json_output::print_errors_with_offset_kind(
                &mut out,
                strip_root.as_deref(),
                suppressed_errors,
                pretty,
                json_version
                    .unwrap_or(flow_common_errors::error_utils::json_output::JsonVersion::JsonV1),
                &stdin_file,
                offset_kind,
                errors,
                warnings,
            )
            .expect("failed to write json errors");
            out.flush().expect("failed to flush json errors");
        };
    match response {
        server_prot::response::StatusResponse::ERRORS {
            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(),

View on GitHub (pinned to f88ac94bcf)

Solutions

  1. Buffer the pipeline so the CLI can finish writing: `flow check-contents file.js --json | cat | head -1`, or drop the early-exiting filter
  2. Write the report to a file and post-process it: `flow check-contents file.js --json > report.json`
  3. If redirecting to a file or device, free disk space or fix the target (df -h; verify the redirect path)
  4. Maintainer: match on the Result and exit quietly with code 141 on ErrorKind::BrokenPipe instead of expect

Example fix

// before
print_errors_with_offset_kind(&mut out, /* ... */)
    .expect("failed to write json errors");

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

Strategy: try-catch

Validate before calling

// If you spawn this CLI, never close its stdout pipe early —
// drain it fully, or kill the child first:
let mut child = std::process::Command::new("flow")
    .args(["check-contents", "--json", "file.js"])
    .stdout(std::process::Stdio::piped())
    .spawn()?;
let mut out = child.stdout.take().unwrap();
let mut buf = Vec::new();
out.read_to_end(&mut buf)?; // consume everything before exiting
child.wait()?;

Try / catch

match print_errors_with_offset_kind(&mut out, /* ... */) {
    Ok(()) => {}
    Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe =>
        std::process::exit(141), // reader gone: exit quietly like C tools
    Err(e) => panic!("failed to write json errors: {e}"),
}

Prevention

When it happens

Trigger: `flow check-contents file.js --json | head -1` while many diagnostics are still being written; piping into `grep -q pattern` which exits on first match; redirecting --json output to a full filesystem or /dev/full (ENOSPC); invoking the CLI with stdout closed (`>&-`).

Common situations: CI scripts and shell pipelines that stream JSON error reports into filters which terminate early; docker/cron contexts where stdout points to /dev/full or a closed fd; pagers or log collectors that stop reading partway through a large report.

Related errors


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