rust-lang/rust · error · FormatDiffError

rustfmt failed with {exit_status}

Error message

rustfmt failed with {exit_status}

What it means

Returned by rustfmt's format-diff tool when the spawned rustfmt process exits with a non-success status. The exit status is interpolated into the message and wrapped as ErrorKind::Other (FormatDiffError::IoError). format-diff feeds git-diff ranges to rustfmt via --file-lines; a non-zero exit means rustfmt rejected the input or hit an internal error.

Source

Thrown at src/tools/rustfmt/src/format-diff/main.rs:112

    let ranges_as_json = json::to_string(ranges).unwrap();

    debug!("Files: {:?}", files);
    debug!("Ranges: {:?}", ranges);

    let rustfmt_var = env::var_os("RUSTFMT");
    let rustfmt = match &rustfmt_var {
        Some(rustfmt) => rustfmt,
        None => OsStr::new("rustfmt"),
    };
    let exit_status = process::Command::new(rustfmt)
        .args(files)
        .arg("--file-lines")
        .arg(ranges_as_json)
        .status()?;

    if !exit_status.success() {
        return Err(FormatDiffError::IoError(io::Error::new(
            io::ErrorKind::Other,
            format!("rustfmt failed with {exit_status}"),
        )));
    }
    Ok(())
}

/// Scans a diff from `from`, and returns the set of files found, and the ranges
/// in those files.
fn scan_diff<R>(
    from: R,
    skip_prefix: u32,
    file_filter: &str,
) -> Result<(HashSet<String>, Vec<Range>), FormatDiffError>
where
    R: io::Read,
{
    let diff_pattern = format!(r"^\+\+\+\s(?:.*?/){{{skip_prefix}}}(\S*)");

View on GitHub (pinned to 7088e4b63a)

Solutions

  1. Run the exact rustfmt invocation from the error's context manually to see the real diagnostic.
  2. Ensure the rustfmt version matches what format-diff expects (nightly for --file-lines).
  3. Fix syntax errors in the files included in the diff ranges.
  4. Validate the --file-lines JSON structure against the expected schema.

Example fix

# before: rustfmt --file-lines fails opaquely

# after: reproduce and read the real error
rustfmt --file-lines file_lines.json --edition 2021 src/lib.rs
echo $?   # inspect rustfmt's own stderr for the cause
Defensive patterns

Strategy: try-catch

Validate before calling

fn file_lines_json_valid(json: &str) -> bool {
    serde_json::from_str::<Vec<serde_json::Value>>(json).is_ok()
}
// Before invoking rustfmt --file-lines, assert file_lines_json_valid(&ranges_as_json).

Try / catch

let status = Command::new(rustfmt).args(files).arg("--file-lines").arg(json).status()?;
if !status.success() {
    // capture rustfmt's own stderr to report the real diagnostic
    return Err(format!("rustfmt failed with {status}").into());
}
Ok(())

Prevention

When it happens

Trigger: Running `rustfmt --file-lines <json>` (typically via git diff integration / format-diff) where rustfmt exits non-zero - syntax error in the ranged file, invalid --file-lines JSON, rustfmt config error, or an internal rustfmt panic. format-diff checks exit_status.success() and raises this error.

Common situations: Unparsable Rust in the diff range; mismatched rustfmt version that does not support --file-lines; nightly-only config option used on stable rustfmt; malformed file-lines JSON; partial edits that confuse the range formatter.

Related errors


AI-assisted analysis of rust-lang/rust@7088e4b63a (2026-08-10). Data as JSON: /api/errors/e3be8e65f98e4716. Report an issue: GitHub.