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

  1. Refresh the diffs for the commit and pass specs derived from its current tree
  2. Verify each requested change (path and content) actually exists in the target commit before calling
  3. Remove the stale/invalid specs from the request and retry
  4. 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

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


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) = groups

View on GitHub (pinned to 58e5313667)