rtk-ai/rtk · error
--numstat -p must parse
Error message
--numstat -p must parse
What it means
`.expect("--numstat -p must parse")` panic: `condense_unified_diff_strict()` returned None on a `git log --numstat -p` fixture. The challenge is a mixed stream: a `--numstat` block (including `-<TAB>-<TAB>b.bin`, the binary marker) precedes the patches, and the condenser must consume numstat facts and the patches together. None indicates the numstat prelude derails header detection.
Solutions
- Teach the strict condenser to skip/collect `--numstat` lines (tab-separated, with `-` placeholder for binary) until the first `commit`/`diff --git` boundary.
- Treat `-<TAB>-<TAB>path` as a binary fact, not a malformed header.
- Verify sub-commit boundaries in `git log -p` streams reset state between patches.
- Add a standalone test for the numstat prelude scanner.
Example fix
// before
let lines = diff.split('\n').collect(); // first line '3\t1\tpath' => no header => None
// after
let lines = skip_numstat_prelude(diff).split('\n').collect(); Defensive patterns
Strategy: validation
Validate before calling
assert!(condense_unified_diff_strict(&numstat_p_fixture)
.map_or(false, |o| o.contains("[file] b.bin (binary)") && o.contains("[file] d/e/f (+1 -0)"))); Type guard
fn can_condense(diff: &str) -> bool { crate::cmds::git::diff_cmd::condense_unified_diff_strict(diff).is_some() } Try / catch
let out = match condense_unified_diff_strict(input) {
Some(o) if o.len() < input.len() => o,
_ => input.to_string(),
}; Prevention
- Whenever combining stat-style flags (-numstat/--stat/--shortstat) with -p, add a mixed-stream fixture.
- Treat tab-separated numeric preludes as facts, never as candidate headers.
- Reset parser state at each commit boundary in log -p streams.
- Note binary markers (`-\t-\tpath`) explicitly in the numstat scanner.
When it happens
Trigger: Calling `condense_unified_diff_strict()` on tests/fixtures/diff/git_log_numstat_p_raw.txt returns None because the leading numstat lines (`added<TAB>deleted<TAB>path`, with `-` for binary) are not recognized, so the parser never enters patch mode and yields None. The expected output mixes `[file] b.bin (binary)` and patch-derived entries like `[file] d/e/f (+1 -0)`.
Common situations: Running `rtk git log --numstat -p`; combining stat-style producers with `-p` patches; changes to numstat/binary-marker handling in the diff filter.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
AI-assisted analysis of rtk-ai/rtk@0924356b4c (2026-09-19).
Data as JSON: /api/errors/94c592317eed63e2.
Report an issue: GitHub.
Appendix: source
Thrown at src/cmds/git/diff_cmd.rs:6611
// `git diff --cached -M --no-prefix`: `diff --git src.txt dst.txt`
// shares no tail; `rename to` names it and the pair may not.
let np = include_str!("../../../tests/fixtures/diff/git_diff_no_prefix_rename_raw.txt");
let out = condense_unified_diff_strict(np).expect("must parse");
assert!(
out.contains("[file] dst.txt (renamed from src.txt) (+1 -0)"),
"got:\n{out}"
);
// `--submodule=diff`: nested column-0 sections under the range.
let sd = include_str!("../../../tests/fixtures/diff/git_diff_submodule_diff_raw.txt");
assert_eq!(
condense_unified_diff_strict(sd).as_deref(),
Some(
"[file] o.txt (+1 -1)\n-k\n+k2\n[file] sub (submodule, 0641fef..19e2e29)\n[file] sub/s.txt (+1 -1)\n-s\n+s2\n[file] sub/sb.bin (binary)"
)
);
// `git log --numstat -p`: `-<TAB>-<TAB>b.bin` before the diff.
let ns = include_str!("../../../tests/fixtures/diff/git_log_numstat_p_raw.txt");
let out = condense_unified_diff_strict(ns).expect("--numstat -p must parse");
assert!(out.contains("[file] b.bin (binary)"), "got:\n{out}");
assert!(out.contains("[file] d/e/f (+1 -0)"), "got:\n{out}");
// `hg log -p --template '{node|short} {desc}\n'`: a prose prologue
// with no `changeset:` line, then hg's echo — prose is not a fact.
let tmpl = include_str!("../../../tests/fixtures/diff/hg_log_p_template_raw.txt");
let out = condense_unified_diff_strict(tmpl).expect("templated hg log must parse");
assert!(out.contains("[file] bin.dat (binary)"), "got:\n{out}");
assert!(out.contains("[file] f.txt (+1 -1)"), "got:\n{out}");
// `LC_ALL=fr_FR.UTF-8 diff -u`: the marker is localized, position
// decides — kept after `-x` and after `+y`.
let fr = include_str!("../../../tests/fixtures/diff/diff_u_fr_no_newline_raw.txt");
assert_eq!(
condense_unified_diff_strict(fr).as_deref(),
Some(
"[file] n2 (+1 -1)\n-x\n\\ Pas de fin de ligne à la fin du fichier\n+y\n\\ Pas de fin de ligne à la fin du fichier"
)
);
// A git binary section followed by another producer's header pairView on GitHub (pinned to 0924356b4c)