gitbutlerapp/gitbutler · error

Stack has no ID

Error message

Stack has no ID

What it means

After finding the stack containing the target branch, the absorb plan reads stack.id to filter hunk assignments by stack. If the stack entry carries no ID, planning aborts with 'Stack has no ID'. This is an internal invariant violation: current stacks are expected to always have IDs, so a None here means legacy or damaged workspace metadata.

Source

Thrown at crates/but-api/src/legacy/absorb.rs:154

                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,
            assigned_stack_id,
        } => {

View on GitHub (pinned to 2497b8007a)

Solutions

  1. Recreate the stack (new stack, then move/recreate the branch in it) so it gets a fresh ID, and retry absorb.
  2. Update GitButler so pending workspace metadata migrations run for the project.
  3. If reproducible on a current version, report it — stacks without IDs should not be reachable.
Defensive patterns

Strategy: try-catch

Validate before calling

fn stack_has_id(stack: &Stack) -> bool {
    stack.id.is_some()
}

Type guard

fn usable_for_absorb(stack: &Stack) -> bool {
    stack.id.is_some() && !stack.heads.is_empty()
}

Try / catch

match absorption_plan_with_perm(ctx, target, perm) {
    Err(err) if err.to_string().contains("Stack has no ID") => {
        rebuild_stack_metadata(ctx)?; // recreate the stack with a fresh ID; do not retry unchanged
    }
    other => other?,
}

Prevention

When it happens

Trigger: absorption_plan_with_perm(Branch) on workspace data where the matching stack's id field is None — older metadata formats, partially migrated projects, or corrupted stack entries from interrupted operations.

Common situations: Projects carried across major GitButler version upgrades without running migrations; manually edited metadata; restored databases.

Related errors


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