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

  1. List current stack heads (`but status` / the workspace stacks API) and pass the exact branch name.
  2. Re-apply the stack containing the branch, then retry.
  3. 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

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


AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17). Data as JSON: /api/errors/29f9074580e42a9a. Report an issue: GitHub.