gitbutlerapp/gitbutler · error
Cannot compute diff specs for stacks
Error message
Cannot compute diff specs for stacks
What it means
Thrown by the diff-spec builder in the `but` crate when a change selection identifying a whole stack (`CliId::Stack`) reaches `push_changes`. Stacks are aggregate selections that must be expanded into their constituent commits/hunks during resolution; the builder only accepts selections resolvable against one repository checkout. Hitting this bail means an unexpanded stack id was passed directly.
Source
Thrown at crates/but/src/utils/diff_specs.rs:88
CliId::Branch(branch) => {
anyhow::bail!("Cannot compute diff specs for branch `{}`", branch.name)
}
CliId::Commit {
commit:
CommitId {
commit_id,
change_id: _,
},
id: _,
} => self.push_changes_from_commit(*commit_id),
CliId::Uncommitted { id: _ } => self.push_changes_from_uncommitted_area(),
// A worktree is expanded into its files during resolution, so the
// builder only ever sees hunks that already come from its own repo.
CliId::Worktree { name, .. } => {
anyhow::bail!("Cannot compute diff specs for worktree `{name}`")
}
CliId::Stack { .. } => {
anyhow::bail!("Cannot compute diff specs for stacks")
}
}
}
pub fn push_changes_from_uncommitted(
&mut self,
uncommitted: &UncommittedHunkOrFile,
) -> anyhow::Result<()> {
// Specs are built against one checkout's repository, so a hunk from
// another would silently address the wrong files. Operations that support
// worktree sources validate the selection and construct the builder via
// [`Self::for_change_source`]; the rest read the main worktree and refuse
// worktree hunks here.
if uncommitted.source != self.source {
if let Some(name) = uncommitted.source.worktree_name() {
anyhow::bail!("Cannot operate on uncommitted changes in worktree {name} yet");
}
anyhow::bail!(View on GitHub (pinned to caf1f223d3)
Solutions
- Run the selection through the standard resolution step that expands stacks into commit-level selections before building specs
- Use the validated operation path that supports the selection kind instead of calling the builder directly
- If reached via an existing command, report a bug with the exact selection you passed
Example fix
// before
let mut builder = DiffSpecBuilder::new(&repo);
builder.push_changes(&CliId::Stack { .. })?;
// after: expand the stack into its commits first
let resolved = resolve_selection(&repo, &cli_id)?; // stack -> its commits
let mut builder = DiffSpecBuilder::new(&repo);
builder.push_changes(&resolved)?; Defensive patterns
Strategy: validation
Validate before calling
if matches!(cli_id, CliId::Stack { .. }) {
let cli_id = resolve_selection(&repo, &cli_id)?; // expands the stack into commits
// ... proceed with the resolved selection
} Type guard
fn is_pushable_diff_spec_source(id: &CliId) -> bool {
!matches!(id, CliId::Worktree { .. } | CliId::Stack { .. })
} Try / catch
match builder.push_changes(&selection) {
Ok(()) => { /* continue */ }
Err(err) if err.to_string().contains("Cannot compute diff specs for stacks") => {
// expand the stack selection and retry
}
Err(err) => return Err(err),
} Prevention
- Route stack selections through resolution before spec computation
- Add unit tests covering every CliId variant your command accepts
When it happens
Trigger: Calling `DiffSpecBuilder::push_changes` with a `CliId::Stack { .. }` value, bypassing the resolution step that flattens a stack into per-commit selections.
Common situations: CLI/TUI commands that accept stack selections for operations that only compute specs from commits or uncommitted hunks; refactors that skip resolution; tests passing raw ids.
Related errors
- Cannot compute diff specs for worktree `{name}`
- Error ${error.response.status}: ${error.message}
- Invalid message format
- Failed to parse diff header
- Invalid source: expected an uncommitted file or branch
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/facb08ba2d01e0d1.
Report an issue: GitHub.