gitbutlerapp/gitbutler · error
Couldn't obtain diff for worktree changes.
Error message
Couldn't obtain diff for worktree changes.
What it means
Thrown when unified_patch_with_filter returns None while computing which hunks to discard, i.e. no unified patch could be produced for the worktree change even though the code path expects one. Without the worktree diff, the library cannot determine remaining hunks, so it aborts. This usually means the change is not a plain text-blob modification.
Solutions
- Verify the target file is a text blob that produces a diff (git diff -- <path>) before hunk discard.
- Re-enumerate worktree changes immediately before discarding to avoid stale descriptors.
- Fall back to whole-file restore when no patch can be computed for the change.
Example fix
// before
let Some(UnifiedPatch::Patch { hunks, .. }) = wt_change.unified_patch_with_filter(...)? else {
bail!("Couldn't obtain diff for worktree changes.")
};
// after
match wt_change.unified_patch_with_filter(...)? {
Some(UnifiedPatch::Patch { hunks, .. }) => discard_hunks(hunks),
Some(_) | None => whole_file_restore(wt_change)?, // fallback
} Defensive patterns
Strategy: fallback
Validate before calling
// verify the change still yields a text patch before hunk discard
let patch = wt_change.unified_patch_with_filter(repo, ctx, &mut filter)?;
let discardable = matches!(patch, Some(UnifiedPatch::Patch { .. })); Try / catch
if let Err(e) = discard_hunks(...) {
if e.to_string().contains("Couldn't obtain diff") {
whole_file_restore(change)?; // safe fallback
} else { return Err(e.into()); }
} Prevention
- Re-enumerate worktree changes right before discarding to avoid stale data
- Restrict line-level discard UI to text files (exclude binary assets)
- Always implement a whole-file fallback path for hunk operations
When it happens
Trigger: Calling hunk-level discard (restore_state_to_worktree with hunks) on a change whose unified_patch_with_filter yields None — e.g. binary files, paths the diff filter rejects, or race where the file changed/vanished between change enumeration and patch computation.
Common situations: User tries to discard lines of a binary or non-UTF-8 file; the file was modified or deleted by another process between listing changes and discarding; custom diff filters exclude the file.
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
- Deletions or additions aren't well-defined for hunk-based…
- Unexpected hunk with neither newlines or oldlines being 0
- Alternative hunks algorithms still didn't produce properly…
- Cannot uncommit hunks that would result in merge conflicts
- Deletions or additions aren't well-defined for hunk-based…
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/3d88a11d57bbea42.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-workspace/src/tree_manipulation/discard_worktree_changes.rs:610
index: &gix::index::State,
context_lines: u32,
) -> anyhow::Result<()> {
let repo = pipeline.repo;
let state_in_worktree = ChangeState {
id: repo.object_hash().null(),
kind: previous_state.kind,
};
let mut diff_filter = but_core::unified_diff::filter_from_state(
repo,
Some(state_in_worktree),
UnifiedPatch::CONVERSION_MODE,
)?;
let Some(UnifiedPatch::Patch {
hunks: hunks_in_worktree,
..
}) = wt_change.unified_patch_with_filter(repo, context_lines, &mut diff_filter)?
else {
bail!("Couldn't obtain diff for worktree changes.")
};
let mut hunks_to_keep: Vec<HunkHeader> = (hunks_in_worktree)
.into_iter()
.map(Into::into)
.filter(|hunk| {
match hunks_to_discard
.iter()
.enumerate()
.find_map(|(idx, hunk_to_discard)| (hunk_to_discard == hunk).then_some(idx))
{
None => true,
Some(idx_to_remove) => {
hunks_to_discard.remove(idx_to_remove);
false
}
}
})View on GitHub (pinned to 58e5313667)