gitbutlerapp/gitbutler · error
Failed to remove specified changes from commit
Error message
Failed to remove specified changes from commit
What it means
After constructing a tree with the requested diffs removed, the code verifies that every requested diff was actually dropped (dropped_diffs empty). If any requested change could not be found/removed in the commit's tree, the operation is considered failed because the result would silently differ from the request.
Solutions
- Refresh the diffs for the commit and pass specs derived from its current tree
- Verify each requested change (path and content) actually exists in the target commit before calling
- Remove the stale/invalid specs from the request and retry
- If the change lives elsewhere (worktree, another commit), uncommit from that source instead
Example fix
// before but.uncommit_changes_no_rebase(commit, stale_specs); // bails: spec not in commit // after let fresh_specs = commit.diff_specs(repo)?; let wanted = fresh_specs.into_iter().filter(|s| user_selected(s)); but.uncommit_changes_no_rebase(commit, wanted);
Defensive patterns
Strategy: validation
Validate before calling
let available = commit.diff_specs(&repo)?; let valid: Vec<_> = requested.into_iter().filter(|s| available.contains(s)).collect(); assert_eq!(valid.len(), requested_count); // all requested specs must exist
Try / catch
match result {
Err(e) if e.to_string().contains("Failed to remove specified changes") => {
// refresh specs from the commit tree and retry once
}
other => other?,
} Prevention
- Always derive DiffSpecs from the commit's current tree, not the worktree
- Invalidate cached selections whenever the underlying commit changes
- Log and drop stale specs instead of passing them through
When it happens
Trigger: Calling uncommit_changes_no_rebase with DiffSpecs that don't match actual content in the commit (wrong paths, stale file hashes, hunks that don't exist in the commit's tree, already-removed changes).
Common situations: Stale UI state where the user selected lines from an older version of the commit; passing file paths that were never part of the commit; hunk specs computed against the worktree rather than the commit tree.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Aborting due to empty branch name
- Branch name ' ' collides with existing branch
- Can only check out local branches under refs/heads, got
- Can only check out local branches under refs/heads or…
- Can only delete local branches under refs/heads, got
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/2027d8de7c57070d.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-workspace/src/commit/uncommit_changes.rs:220
commit: impl ToCommitSelector,
changes: impl IntoIterator<Item = DiffSpec>,
context_lines: u32,
) -> Result<Selector> {
let (commit_selector, commit) = editor.find_selectable_commit(commit)?;
if commit.clone().attach(editor.repo()).is_conflicted() {
bail!("Cannot uncommit changes from a conflicted commit")
}
let (tree_without_changes, dropped_diffs) = create_tree_without_diff(
editor.repo(),
ChangesSource::Commit { id: commit.id },
changes,
context_lines,
)?;
if !dropped_diffs.is_empty() {
bail!("Failed to remove specified changes from commit");
}
let new_commit_id = {
let mut new_commit = commit.clone();
new_commit.tree = tree_without_changes;
editor.new_commit(new_commit, DateMode::CommitterUpdateAuthorKeep)?
};
editor.replace(commit_selector, Step::new_pick(new_commit_id))?;
Ok(commit_selector)
}
fn group_sources_by_commit(
sources: impl IntoIterator<Item = UncommitChangesSource>,
) -> Vec<GroupedUncommitChanges> {
let mut groups = Vec::<GroupedUncommitChanges>::new();
for source in sources {
if let Some(group) = groupsView on GitHub (pinned to 58e5313667)