gitbutlerapp/gitbutler · error
Stack has no ID
Error message
Stack has no ID
What it means
Immediately after finding the matching stack in `absorption_plan_with_perm`, the code requires `stack.id` to be `Some`. A matched stack with `None` id means in-memory workspace data was built without persisting/assigning a stack identifier — an internal invariant breach rather than bad user input. The error surfaces as "Stack has no ID".
Solutions
- Re-read/refresh workspace metadata from the database so stack ids are populated, then retry.
- Run `but status` / restart the operation to force a fresh workspace build.
- Check for mixed tool versions touching the same workspace; upgrade the CLI/app to matching versions.
- If persistent, report as a bug — a persisted applied stack should always carry an id.
Example fix
// before let plan = absorption_plan(ctx, branch)?; // may hit Stack has no ID // after ctx.reload_workspace()?; // refresh persisted metadata (ids present) let plan = absorption_plan(ctx, branch)?;
Defensive patterns
Strategy: try-catch
Validate before calling
// a persisted applied stack must carry an id; surface anomalies early
if let Some(stack) = ws.stacks.iter().find(|s| matches_branch(s)) {
if stack.id.is_none() {
eprintln!("BUG: matched stack has no id; workspace metadata may be corrupt");
}
} Try / catch
match absorption_plan(ctx, branch) {
Err(e) if e.to_string().contains("Stack has no ID") => {
ctx.reload_workspace()?; // refresh persisted metadata and retry once
absorption_plan(ctx, branch)?;
}
other => other?,
} Prevention
- Keep GitButler CLI/app versions in sync across tooling touching the same workspace
- Re-read workspace state from the DB rather than reusing in-memory snapshots
- Report persistent occurrences as bugs — applied stacks should always have ids
When it happens
Trigger: Calling absorption_plan against a workspace whose stack entries were constructed without an id (e.g. un-persisted stacks from a stale metadata read, partially migrated workspace state, or a bug in workspace building where `stack.id` was never set).
Common situations: Corrupted or mid-migration GitButler workspace metadata; operating on workspace data produced by an older/newer tool version with a different persistence schema; transient in-memory state not yet written to the DB.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Could not find associated reference name
- a committed transaction always materializes a workspace
- Ad-hoc (single-branch) branch moves are not supported…
- another pre-commit hook is already using the repository…
- askpass broker must be initialized
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/487a19aafadb882c.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-api/src/legacy/absorb.rs:147
// Get the stack ID for this branch
let workspace = crate::legacy::workspace::head_info(ctx)?;
// Find the stack that contains this branch
let stack = workspace
.stacks
.iter()
.find(|stack| {
stack.segments.iter().any(|segment| {
segment
.ref_info
.as_ref()
.is_some_and(|ri| ri.ref_name.shorten() == branch_name.as_bytes())
})
})
.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 58e5313667)