{"record":{"id":"46936165c04d6baa","repo":"gitbutlerapp/gitbutler","slug":"cannot-compute-diff-specs-for-branch","errorCode":null,"errorMessage":"Cannot compute diff specs for branch `{}`","messagePattern":"Cannot compute diff specs for branch `(.+?)`","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but/src/utils/diff_specs.rs","lineNumber":71,"sourceCode":"            CliId::UncommittedHunkOrFile(uncommitted) => {\n                self.push_changes_from_uncommitted(uncommitted)\n            }\n            CliId::PathPrefix {\n                id: _,\n                hunks,\n                source: _,\n            } => self.push_changes_from_path_prefix(hunks),\n            CliId::CommittedFile {\n                committed_file:\n                    CommittedFileId {\n                        commit_id,\n                        path,\n                        change_id: _,\n                    },\n                id: _,\n            } => self.push_changes_from_committed_file(*commit_id, path.as_ref()),\n            CliId::Branch(branch) => {\n                anyhow::bail!(\"Cannot compute diff specs for branch `{}`\", branch.name)\n            }\n            CliId::Commit {\n                commit:\n                    CommitId {\n                        commit_id,\n                        change_id: _,\n                    },\n                id: _,\n            } => self.push_changes_from_commit(*commit_id),\n            CliId::Uncommitted { id: _ } => self.push_changes_from_uncommitted_area(),\n            // A worktree is expanded into its files during resolution, so the\n            // builder only ever sees hunks that already come from its own repo.\n            CliId::Worktree { name, .. } => {\n                anyhow::bail!(\"Cannot compute diff specs for worktree `{name}`\")\n            }\n            CliId::Stack { .. } => {\n                anyhow::bail!(\"Cannot compute diff specs for stacks\")\n            }","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but/src/utils/diff_specs.rs#L53-L89","documentation":"DiffSpecBuilder::push_changes_id turns a resolved change selection into diff specs, but only handles hunks, path prefixes, committed files, commits, and uncommitted changes. Branch-level selections (CliId::Branch) are supposed to be expanded into files during resolution; if one reaches the builder unexpanded there is no single diff spec for a whole branch, so it bails with the branch name.","triggerScenarios":"Calling the diff-spec computation with a CliId::Branch that skipped the expansion step - a new caller feeding unresolved ids, or a resolution-order change that lets branch ids through to the builder.","commonSituations":"Programmatic users of the change-selection APIs, refactors that bypass the resolver, new command implementations reusing DiffSpecBuilder directly.","solutions":["Expand the branch into its files/commits during resolution before computing diff specs","Use a branch-diff API instead of the hunk/file diff-spec builder","If you maintain the caller, match on CliId and reject Branch early with a clearer message"],"exampleFix":"// before\nfor id in ids {\n    builder.push_changes_from_id(id)?; // panics-bails on CliId::Branch\n}\n// after\nfor id in ids {\n    if let CliId::Branch(b) = id { return Err(anyhow::anyhow!(\"expand branch `{}` first\", b.name)); }\n    builder.push_changes_from_id(id)?;\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"fn supports_diff_specs(id: &CliId) -> bool {\n    !matches!(id, CliId::Branch { .. })\n}\n// guard every id before building specs:\nassert!(ids.iter().all(|id| supports_diff_specs(id)), \"branch ids must be expanded first\");","tryCatchPattern":null,"preventionTips":["Never hand raw CliId::Branch values to DiffSpecBuilder - expand them during resolution","Centralize id resolution so the expansion step cannot be skipped","Add a unit test asserting branch ids are rejected before the builder runs"],"tags":["diff","selection","branch","api-misuse","internal"],"backgroundTag":"unsupported-selection-type","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}