gitbutlerapp/gitbutler · error
Could not discard all selected changes: {refused_paths}
Error message
Could not discard all selected changes: {refused_paths} What it means
`but discard` feeds the selected uncommitted changes (DiffSpecs) to `but_workspace::discard_workspace_changes`, which re-reads the current worktree diff and refuses any spec whose path (and rename previous_path) no longer matches a live worktree change (see the `dropped.push(spec)` branch in discard_worktree_changes.rs). The CLI bails listing the refused paths; the bail aborts the surrounding transaction, so no partial discard is committed.
Source
Thrown at crates/but/src/command/legacy/discard.rs:511
let new_commit = tx.discard_changes_from_commit(source.commit_id, changes)?;
DiscardOutcome::CommittedFiles {
source,
paths,
new_commit: new_commit.into(),
}
}
ExecutableDiscardOperation::Uncommitted { paths, changes } => {
let refused = but_workspace::discard_workspace_changes(
tx.repo(),
changes,
tx.context_lines(),
)?;
if !refused.is_empty() {
let refused_paths = refused
.iter()
.map(|change| change.path.as_bstr())
.join(", ");
bail!("Could not discard all selected changes: {refused_paths}");
}
DiscardOutcome::Uncommitted { paths }
}
};
Ok(Commit(outcome))
},
)?;
if let DiscardOutcome::Commits {
replaced_commits, ..
} = &mut outcome
{
*replaced_commits = std::mem::take(&mut ws.replaced_commits);
}
Ok((outcome, ws))
}View on GitHub (pinned to caf1f223d3)
Solutions
- Re-select and re-run the discard so the diff specs are computed from the current worktree state.
- Check `but status` / `git status`: the refused paths may already be gone or changed; discard what remains individually.
- Close other programs touching the worktree (editors, file watchers, other `but` instances) and retry.
Defensive patterns
Strategy: retry
Validate before calling
# Refresh the selection right before discarding; refused paths are ones no longer in the current diff
but status | grep -F '<path>' || { echo 'path no longer dirty'; exit 2; } Try / catch
// When calling discard APIs programmatically, recompute specs and retry once on refusal
let refused = discard_workspace_changes(repo, specs.clone(), context_lines)?;
if !refused.is_empty() {
let specs = recompute_specs_from_current_status(repo)?; // selection was stale
let refused = discard_workspace_changes(repo, specs, context_lines)?;
// surface any remaining refusals to the caller
} Prevention
- Compute diff specs immediately before discarding, never from cached status.
- Avoid concurrent worktree mutations (editors, watchers, other `but` instances) while a discard runs.
- After a refusal, refresh `but status` and discard the remaining paths individually.
When it happens
Trigger: The worktree changed between computing the diff specs (e.g. an interactive selection) and executing the discard: the file was reverted, committed, or its hunks shifted so no matching entry exists in the fresh worktree status; rename specs whose previous_path no longer matches are also refused.
Common situations: Concurrent editors/IDE formatters touching files while discarding; running the same discard selection twice (the second run's specs are stale); another GitButler client or agent mutating the worktree between selection and execution.
Related errors
- When using OpenAI in a bring your own key configuration, you
- Failed to parse diff header
- Cannot compute diff specs for worktree `{name}`
- Failed to communicate with LM Studio server: ${error instanc
- Invalid response: ${JSON.stringify(result)}
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/4e0bd2405b0e1c00.
Report an issue: GitHub.