{"record":{"id":"fc27d22626f3a3e2","repo":"gitbutlerapp/gitbutler","slug":"unexpected-hunk-with-neither-newlines-or-oldlines","errorCode":null,"errorMessage":"Unexpected hunk with neither newlines or oldlines being 0","messagePattern":"Unexpected hunk with neither newlines or oldlines being 0","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-core/src/tree/mod.rs","lineNumber":471,"sourceCode":"                     new_start,\n                     new_lines,\n                 }| {\n                    if old_lines == 0 {\n                        Ok(HunkHeader {\n                            old_start: 0,\n                            old_lines: 0,\n                            new_start,\n                            new_lines,\n                        })\n                    } else if new_lines == 0 {\n                        Ok(HunkHeader {\n                            old_start,\n                            old_lines,\n                            new_start: 0,\n                            new_lines: 0,\n                        })\n                    } else {\n                        bail!(\"Unexpected hunk with neither newlines or oldlines being 0\");\n                    }\n                },\n            )\n            .collect::<Result<_, _>>()?;\n        let (hunks_to_commit, rejected) =\n            to_additive_hunks_fallback(hunks, worktree_hunks, worktree_hunks_no_context);\n        if !in_order(&hunks_to_commit) {\n            bail!(\n                \"Alternative hunks algorithms still didn't produce properly ordered hunks or saw duplicate inputs: {hunks_to_commit:?}\"\n            );\n        }\n        (hunks_to_commit, rejected)\n    } else {\n        (hunks_to_commit, rejected)\n    };\n    Ok(res)\n}\n","sourceCodeStart":453,"sourceCodeEnd":489,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-core/src/tree/mod.rs#L453-L489","documentation":"When GitButler computes which hunks to commit, the primary algorithm must produce hunks in order; if not, it retries with `to_additive_hunks_fallback`, which only understands purely additive (old_lines == 0) or purely subtractive (new_lines == 0) hunks. A selected hunk with both counts non-zero (a modification) reaching this fallback trips the bail — the fallback was never designed for replacement hunks.","triggerScenarios":"Partially staging hunks such that the primary algorithm emits out-of-order/duplicate hunks and the retry set contains a modification hunk — typically hand-picked hunk selections on files with interleaved adds, deletes and edits.","commonSituations":"Selecting individual modified hunks in the GitButler UI on files with mixed changes; hunk selections produced after external tools (formatters, editors) race the diff computation.","solutions":["Commit the whole file instead of individual hunks for the affected file.","Change the hunk selection (e.g. include the full modification hunk rather than splitting around it) and retry.","Update GitButler — hunk-selection algorithms are actively improved and this is a known class of fallback limitation.","Capture the hunk headers (logged at info level before the fallback runs) and file a bug report."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":"// Rust — a selected hunk the fallback algorithm can handle must be purely additive or purely subtractive\nfn is_pure_hunk(h: &but_diff::HunkHeader) -> bool {\n    h.old_lines == 0 || h.new_lines == 0\n}","tryCatchPattern":"// Fall back to committing the whole file when hunk-level commit fails\nmatch commit_selected_hunks(path, hunks) {\n    Ok(()) => Ok(()),\n    Err(err) if err.to_string().contains(\"Unexpected hunk\") => {\n        tracing::warn!(\"hunk fallback failed for {path}; committing whole file\");\n        commit_whole_file(path)\n    }\n    Err(err) => Err(err),\n}","preventionTips":["Let the worktree settle (no concurrent editor/format-on-save) before making hunk-level selections.","Prefer selecting whole modification hunks over split fragments around them.","Keep GitButler updated; capture the info-level log with hunk headers when reporting."],"tags":["diff","hunks","partial-staging","internal-algorithm"],"backgroundTag":"diff-hunk-processing","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}