{"record":{"id":"487a19aafadb882c","repo":"gitbutlerapp/gitbutler","slug":"stack-has-no-id","errorCode":null,"errorMessage":"Stack has no ID","messagePattern":"Stack has no ID","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-api/src/legacy/absorb.rs","lineNumber":147,"sourceCode":"\n            // Get the stack ID for this branch\n            let workspace = crate::legacy::workspace::head_info(ctx)?;\n\n            // Find the stack that contains this branch\n            let stack = workspace\n                .stacks\n                .iter()\n                .find(|stack| {\n                    stack.segments.iter().any(|segment| {\n                        segment\n                            .ref_info\n                            .as_ref()\n                            .is_some_and(|ri| ri.ref_name.shorten() == branch_name.as_bytes())\n                    })\n                })\n                .ok_or_else(|| anyhow::anyhow!(\"Branch not found: {branch_name}\"))?;\n\n            let stack_id = stack.id.ok_or_else(|| anyhow::anyhow!(\"Stack has no ID\"))?;\n\n            // Filter assignments to just this stack\n            let candidates: Vec<AbsorbCandidate> = all_assignments\n                .into_iter()\n                .filter(|a| a.stack_id == Some(stack_id))\n                .map(Into::into)\n                .collect();\n\n            if candidates.is_empty() {\n                anyhow::bail!(\"No uncommitted changes assigned to branch: {branch_name}\");\n            }\n\n            (candidates, dependencies)\n        }\n        AbsorptionTarget::TreeChanges {\n            changes,\n            assigned_stack_id,\n        } => {","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-api/src/legacy/absorb.rs#L129-L165","documentation":"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\".","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nlet plan = absorption_plan(ctx, branch)?; // may hit Stack has no ID\n// after\nctx.reload_workspace()?; // refresh persisted metadata (ids present)\nlet plan = absorption_plan(ctx, branch)?;","handlingStrategy":"try-catch","validationCode":"// a persisted applied stack must carry an id; surface anomalies early\nif let Some(stack) = ws.stacks.iter().find(|s| matches_branch(s)) {\n    if stack.id.is_none() {\n        eprintln!(\"BUG: matched stack has no id; workspace metadata may be corrupt\");\n    }\n}","typeGuard":null,"tryCatchPattern":"match absorption_plan(ctx, branch) {\n    Err(e) if e.to_string().contains(\"Stack has no ID\") => {\n        ctx.reload_workspace()?; // refresh persisted metadata and retry once\n        absorption_plan(ctx, branch)?;\n    }\n    other => other?,\n}","preventionTips":["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"],"tags":["git","internal-state","workspace","rust"],"backgroundTag":"internal-invariant-violation","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}