{"record":{"id":"7157ff795ac91ddd","repo":"Hmbown/CodeWhale","slug":"incomplete-pr-diff-a-file-patch-is-missing","errorCode":null,"errorMessage":"Incomplete PR diff: a file patch is missing","messagePattern":"Incomplete PR diff: a file patch is missing","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/review_pr.rs","lineNumber":197,"sourceCode":"                    remaining.1 -= 1;\n                    additions += 1;\n                }\n                Some(b'-') if remaining.0 > 0 => {\n                    remaining.0 -= 1;\n                    deletions += 1;\n                }\n                Some(b' ') if remaining.0 > 0 && remaining.1 > 0 => {\n                    remaining.0 -= 1;\n                    remaining.1 -= 1;\n                }\n                Some(b'\\\\') => {}\n                _ => bail!(\"Incomplete PR diff: a text hunk is truncated or malformed\"),\n            }\n            continue;\n        }\n        if line.starts_with(\"diff --git \") {\n            if files > 0 && !has_patch {\n                bail!(\"Incomplete PR diff: a file patch is missing\");\n            }\n            files += 1;\n            has_patch = false;\n            has_full_index = false;\n        } else if line.starts_with(\"index \") {\n            has_full_index = full_index_objects(line);\n        } else if let Some((_, old, new)) = super::review_hunks::parse_hunk_header(line) {\n            remaining = (old, new);\n            has_patch = true;\n        } else if line.starts_with(\"@@\") {\n            bail!(\"Incomplete PR diff: malformed hunk header\");\n        } else if [\n            \"new file mode \",\n            \"deleted file mode \",\n            \"old mode \",\n            \"new mode \",\n            \"rename from \",\n            \"rename to \",","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tools/review_pr.rs#L179-L215","documentation":"While scanning the diff, complete_file_set requires every `diff --git <a> <b>` file header to be followed by an actual patch body (a valid @@ hunk or binary metadata). When a new file header appears while the previous file (files > 0) never produced has_patch, the previous file's patch is missing entirely and the review aborts.","triggerScenarios":"A `diff --git` header is present but its body was dropped — e.g. truncation removed everything after the header, or `git diff` was run with flags limiting per-file output for a file that changed.","commonSituations":"Very large diffs clipped by output limits; renames/mode-only changes filtered out by diff options; external diff drivers suppressing output for specific file types; corrupted tool output pipelines.","solutions":["Re-run with plain git diff options: `git diff --no-ext-diff --no-color base...head` and confirm every `diff --git` header has a following `index`/hunk body","Check that the file count in the error matches view.changed_files; if fewer, the fetch truncated and the diff must be re-fetched completely","Disable external diff/renames filters for the review path (the tool already passes --no-ext-diff; ensure no repo config overrides it via diff.external)"],"exampleFix":"// before: repo config with an external diff driver\ngit config diff.external /usr/bin/custom-diff\n// after: unset it so --no-ext-diff has nothing to override\ngit config --unset diff.external","handlingStrategy":"validation","validationCode":"// every 'diff --git' header must be followed by an 'index' line\nlet lines: Vec<&str> = diff.lines().collect();\nfor (i, l) in lines.iter().enumerate() {\n    if l.starts_with(\"diff --git \") {\n        assert!(lines.get(i + 1).map_or(false, |n| n.starts_with(\"index \")), \"file patch missing after header at line {}\");\n    }\n}","typeGuard":null,"tryCatchPattern":"match diff_with(&view) {\n    Err(e) if e.to_string().contains(\"file patch is missing\") => eprintln!(\"refetch complete diff; check output limits and external diff drivers\"),\n    other => other?,\n}","preventionTips":["Unset diff.external / diff.<driver>.command in the review checkout","Raise any output truncation limits used by the wrapping process","Verify fetched diff contains exactly view.changed_files 'diff --git' headers"],"tags":["git","diff","data-integrity","truncation"],"backgroundTag":"schema-validation-failed","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}