gitbutlerapp/gitbutler · error

BranchNotFound

BranchNotFound

Error message

branch with ID {stack_id} not found

What it means

`apply_stack_order_updates` processes requested stack order changes; for each update it requires a stack with that id to exist AND be in the workspace (`is_in_workspace()`). Otherwise it throws "branch with ID {stack_id} not found" with code `BranchNotFound`. The id came from the caller's update list, so it references a stack that is missing or unapplied.

Solutions

  1. Re-fetch the current workspace stack list and use only ids that are currently applied/in-workspace.
  2. Remove the stale id from your update list and retry with the refreshed ids.
  3. Re-apply the stack to the workspace first if reordering an unapplied branch is intended.
  4. Avoid concurrent workspace mutations while submitting order updates.

Example fix

// before
let ids = cached_stack_ids; // may include unapplied stacks
update_stack_order(ctx, ids.iter().enumerate().map(|(i, id)| Update { id: Some(*id), order: Some(i as u32), .. }));
// after
let ws = ctx.workspace()?; // fresh state
let ids: Vec<_> = ws.stacks.iter().filter(|s| s.is_in_workspace()).map(|s| s.id.unwrap()).collect();
update_stack_order(ctx, ids.iter().enumerate().map(|(i, id)| Update { id: Some(*id), order: Some(i as u32), .. }));
Defensive patterns

Strategy: validation

Validate before calling

// only submit order updates for stacks currently in the workspace
let ws = ctx.workspace()?;
let valid: Vec<_> = updates.into_iter()
    .filter(|u| u.id.map(|id| ws.stacks.iter().any(|s| s.id == Some(id) && s.is_in_workspace())).unwrap_or(true))
    .collect();

Type guard

fn is_in_workspace(stacks: &[Stack], id: StackId) -> bool {
    stacks.iter().any(|s| s.id == Some(id) && s.is_in_workspace())
}

Try / catch

match update_stack_order(ctx, updates) {
    Err(e) if e.to_string().contains("BranchNotFound") => {
        let ws = ctx.workspace()?; // refresh and retry with current ids
        update_stack_order(ctx, refresh_ids(ws, updates))?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling update_stack_order (reorder) with a StackId that is not in the workspace — unapplied stack, deleted stack, stale id from an outdated workspace snapshot; also reached via stack_order_updates_reject_unapplied_stacks which intentionally rejects unapplied stacks.

Common situations: Drag-and-drop UI reordering a stale list while another pane unapplied the stack; automation using ids from an old `but` output; concurrent modifications to workspace order.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/256b42c1d9945447. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-api/src/legacy/virtual_branches.rs:306

    }

    Ok(())
}

fn apply_stack_order_updates(
    workspace: &mut but_core::ref_metadata::Workspace,
    updates: Vec<BranchUpdateRequest>,
) -> Result<bool> {
    let mut requested_orders = HashMap::new();

    for update in updates {
        let stack_id = update.id.context("BUG(opt-stack-id)")?;
        if !workspace
            .stacks
            .iter()
            .any(|stack| stack.id == stack_id && stack.is_in_workspace())
        {
            return Err(anyhow!("branch with ID {stack_id} not found")
                .context(but_error::Code::BranchNotFound));
        }

        if let Some(order) = update.order {
            requested_orders.insert(stack_id, order);
        }
    }

    if requested_orders.is_empty() {
        return Ok(false);
    }

    let original_stack_ids = workspace
        .stacks
        .iter()
        .map(|stack| stack.id)
        .collect::<Vec<_>>();
    let original_orders = original_stack_ids

View on GitHub (pinned to 58e5313667)