facebook/flow · error

failed to flush errors

Error message

failed to flush errors

What it means

The JSON output of a foreground check is buffered in a std::io::BufWriter; this expect guards the final flush. Small reports fit in the buffer, so write! succeeds into memory and the real I/O error (BrokenPipe from a closed consumer, ENOSPC from a full disk, EIO) surfaces only at this flush — making it the most common crash point of the three JSON-output expects.

Source

Thrown at rust_port/crates/flow_cli/src/foreground_check_commands.rs:89

                version.clone().unwrap_or(json_output::JsonVersion::JsonV1),
                &None,
                offset_kind,
                errors,
                warnings,
            );
            let finish_formatting = move |profiling_props| {
                let res = get_json(profiling_props);
                let stdout = std::io::stdout();
                let mut out = std::io::BufWriter::new(stdout.lock());
                use std::io::Write;
                if pretty {
                    write!(out, "{}", flow_hh_json::json_to_multiline(&res))
                        .expect("failed to write errors");
                } else {
                    write!(out, "{}", flow_hh_json::json_string_of_value(&res))
                        .expect("failed to write errors");
                }
                out.flush().expect("failed to flush errors");
            };
            Box::new(move |profiling| {
                let profiling_props = match profiling {
                    Some(serde_json::Value::Object(profiling_props)) => {
                        profiling_props.into_iter().collect()
                    }
                    Some(_) | None => vec![],
                };
                finish_formatting(profiling_props);
            })
        }
        Printer::Cli(flags) => {
            let errors = suppressed_errors
                .iter()
                .fold(errors.clone(), |mut acc, (error, _)| {
                    acc.add(error.clone());
                    acc
                });

View on GitHub (pinned to 5c86586199)

Solutions

  1. Avoid short-lived pipe consumers; write to a file instead.
  2. Ensure the redirect target has space and the consumer reads to EOF.
  3. Maintainer fix: match on flush error kind — BrokenPipe means the consumer is gone, exit 0 quietly; report anything else.

Example fix

// before
out.flush().expect("failed to flush errors");

// after
if let Err(e) = out.flush() {
    if e.kind() != std::io::ErrorKind::BrokenPipe {
        panic!("failed to flush errors: {}", e);
    }
    std::process::exit(0);
}
Defensive patterns

Strategy: try-catch

Try / catch

if let Err(e) = out.flush() {
    if e.kind() == std::io::ErrorKind::BrokenPipe {
        std::process::exit(0);
    }
    panic!("failed to flush errors: {}", e);
}

Prevention

When it happens

Trigger: Any `flow check-contents --json ... | head ...` style pipeline where output fits the BufWriter buffer: the writes 'succeed', then flush fails when the closed pipe or full disk is finally hit.

Common situations: Piping status/check JSON into pagers or filters that quit early; redirecting to a filesystem at capacity; running with stdout closed.

Related errors


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