gitbutlerapp/gitbutler · error
Cannot compute diff specs for worktree `{name}`
Error message
Cannot compute diff specs for worktree `{name}` What it means
Thrown by the diff-spec builder in the `but` crate when a change selection identifying a whole worktree (`CliId::Worktree`) reaches `push_changes`. By design, worktree selections are expanded into their individual files during selection resolution, so the builder only ever expects hunks that already come from its own repository. Seeing this error means a caller passed an unresolved worktree id straight to the builder, bypassing the resolution step.
Source
Thrown at crates/but/src/utils/diff_specs.rs:85
},
id: _,
} => self.push_changes_from_committed_file(*commit_id, path.as_ref()),
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() {View on GitHub (pinned to caf1f223d3)
Solutions
- Route the selection through the standard resolution step that expands worktrees into file-level selections before calling push_changes
- If the operation supports worktree sources, validate the selection and construct the builder via `DiffSpecBuilder::for_change_source` instead of the main-worktree constructor
- If you never call the builder directly, report a bug against the command that produced the unresolved worktree selection
Example fix
// before let mut builder = DiffSpecBuilder::new(&repo); builder.push_changes(&cli_id)?; // cli_id is CliId::Worktree // after: expand aggregate selections during resolution first let resolved = resolve_selection(&repo, &cli_id)?; // worktree -> its files/hunks let mut builder = DiffSpecBuilder::new(&repo); builder.push_changes(&resolved)?;
Defensive patterns
Strategy: validation
Validate before calling
// before computing diff specs, expand aggregate selections
if matches!(cli_id, CliId::Worktree { .. } | CliId::Stack { .. }) {
let cli_id = resolve_selection(&repo, &cli_id)?; // expands worktree/stack into files
// ... 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 worktree") => {
// selection was not resolved; expand it and retry
}
Err(err) => return Err(err),
} Prevention
- Always run selection resolution before handing ids to DiffSpecBuilder
- Use exhaustive matches over CliId in callers so new aggregate variants fail at compile time, not at runtime
When it happens
Trigger: Calling `DiffSpecBuilder::push_changes` (or a wrapper around it) with a `CliId::Worktree { name, .. }` value instead of the resolved per-file/per-hunk selections, e.g. a new CLI command or TUI flow that forwards raw CLI selections directly into diff-spec computation.
Common situations: New commands that accept arbitrary `CliId` values but forget to run the resolver that expands worktrees into files; refactors that route selections around the resolution step; unit tests that hand raw ids to the builder.
Related errors
- Cannot compute diff specs for stacks
- Cannot operate on uncommitted changes in worktree {name} yet
- Invalid token format
- Login token expired. Please log in to GitButler again.
- HTTP Error ${response.statusText}: ${text}
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/4c4ca45298347785.
Report an issue: GitHub.