gitbutlerapp/gitbutler · error
Branch not found: {branch_name}
Error message
Branch not found: {branch_name} What it means
absorption_plan_with_perm with AbsorptionTarget::Branch scans the workspace's stacks for one whose heads include the given branch name (exact short-name comparison) and bails 'Branch not found' when no stack contains it. The plan needs the owning stack's ID to filter hunk assignments, so a branch outside the current stacks cannot be targeted.
Source
Thrown at crates/but-api/src/legacy/absorb.rs:152
ChangesSource::Head,
true,
perm.read_permission(),
)?;
let all_assignments = worktree_changes.assignments;
let dependencies = worktree_changes.dependencies;
// Get the stack ID for this branch
let stacks = crate::legacy::workspace::stacks(ctx, None)?;
// Find the stack that contains this branch
let stack = stacks
.iter()
.find(|s| {
s.heads
.iter()
.any(|h| h.name.to_str().map(|n| n == branch_name).unwrap_or(false))
})
.ok_or_else(|| anyhow::anyhow!("Branch not found: {branch_name}"))?;
let stack_id = stack.id.ok_or_else(|| anyhow::anyhow!("Stack has no ID"))?;
// Filter assignments to just this stack
let candidates: Vec<AbsorbCandidate> = all_assignments
.into_iter()
.filter(|a| a.stack_id == Some(stack_id))
.map(Into::into)
.collect();
if candidates.is_empty() {
anyhow::bail!("No uncommitted changes assigned to branch: {branch_name}");
}
(candidates, dependencies)
}
AbsorptionTarget::TreeChanges {
changes,View on GitHub (pinned to 2497b8007a)
Solutions
- List current stack heads (`but status` / the workspace stacks API) and pass the exact branch name.
- Re-apply the stack containing the branch, then retry.
- If absorbing loose file changes, prefer the TreeChanges target with an explicit file selection instead of a branch target.
Example fix
// Rust caller: validate before planning
let stacks = workspace::stacks(ctx, None)?;
let is_head = stacks.iter().any(|s| {
s.heads.iter().any(|h| h.name.to_str() == Some(branch_name.as_str()))
});
anyhow::ensure!(is_head, "branch {branch_name} is not an applied stack head");
let plan = absorption_plan_with_perm(ctx, AbsorptionTarget::Branch { branch_name }, perm)?; Defensive patterns
Strategy: validation
Validate before calling
fn branch_is_stack_head(stacks: &[Stack], branch: &str) -> bool {
stacks.iter().any(|s| {
s.heads
.iter()
.any(|h| h.name.to_str().map(|n| n == branch).unwrap_or(false))
})
} Prevention
- Populate absorb target pickers from live stack heads only.
- Re-check names after renames or unapplies before planning absorption.
- Use short names, never full refnames or remote-prefixed names.
When it happens
Trigger: Calling absorb/absorption_plan with branch_name that is not a head of any current stack — typos, branches belonging to unapplied or archived stacks, remote-only names, or full refnames instead of short names.
Common situations: Absorbing into a branch whose stack was unapplied; passing origin/<name>; stale UI state after a branch rename; scripts reusing old branch names.
Related errors
- Stack has no ID
- Unable to determine target commit for unassigned change: {}
- BranchNotFound
- No stacks found in the workspace
- No uncommitted changes assigned to branch: {branch_name}
AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17).
Data as JSON: /api/errors/29f9074580e42a9a.
Report an issue: GitHub.