{"record":{"id":"c185c2f484d7161d","repo":"gitbutlerapp/gitbutler","slug":"missing-stack-stack-id-for-head-name","errorCode":null,"errorMessage":"Missing stack '{stack_id}' for head '{name}'","messagePattern":"Missing stack '(.+?)' for head '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/but-meta/src/legacy/storage.rs","lineNumber":511,"sourceCode":"            },\n        );\n    }\n\n    for head in heads {\n        let VbStackHead {\n            stack_id,\n            position: _, // previously set based on vec position\n            name,\n            head_sha,\n            pr_number,\n            archived,\n            review_id,\n        } = head;\n        let stack_id = StackId::from_str(stack_id)\n            .with_context(|| format!(\"Invalid stack id '{stack_id}'\"))?;\n        let stack = branches\n            .get_mut(&stack_id)\n            .ok_or_else(|| anyhow!(\"Missing stack '{stack_id}' for head '{name}'\"))?;\n        stack.heads.push(StackBranch {\n            head: gix::ObjectId::from_str(head_sha)\n                .with_context(|| format!(\"Invalid head sha '{head_sha}' on '{name}'\"))?,\n            name: name.clone(),\n            pr_number: pr_number\n                .map(usize::try_from)\n                .transpose()\n                .with_context(|| {\n                    format!(\n                        \"Invalid pr_number '{}' on stack '{stack_id}'\",\n                        pr_number.unwrap_or_default(),\n                    )\n                })?,\n            archived: *archived,\n            review_id: review_id.clone(),\n        });\n    }\n","sourceCodeStart":493,"sourceCodeEnd":529,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-meta/src/legacy/storage.rs#L493-L529","documentation":"Thrown during legacy metadata migration in crates/but-meta/src/legacy/storage.rs: while converting the old virtual_branches.json heads list, a head entry references a stack_id that does not exist in the stacks map built from the same file. The migration refuses to attach the head to a nonexistent stack, which means the persisted legacy state is internally inconsistent (corruption or a hand-edited file).","triggerScenarios":"Loading a legacy virtual_branches.json whose heads array contains a head with stack_id absent from the stacks section; truncated or hand-edited metadata; files written by a buggy older version that desynchronized stacks and heads.","commonSituations":"Users upgrading from very old GitButler versions with damaged workspace metadata; manually edited or merged .gitbutler metadata files; a crash during an old metadata write leaving stacks and heads out of sync.","solutions":["Restore the workspace metadata from backup (the file is versioned; check history or the automatic snapshots) and retry.","If no backup exists, archive the repo's .gitbutler metadata aside and let GitButler reinitialize the workspace, then recreate virtual branches.","Inspect virtual_branches.json: cross-check every heads[].stack_id against the stacks[].id values to locate the orphan.","Report the file (sanitized) to GitButler — an internally inconsistent file usually indicates an old writer bug worth tracking."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before migrating legacy metadata, cross-check head -> stack references\nuse std::collections::HashSet;\n\nfn heads_reference_existing_stacks(heads: &[LegacyHead], stacks: &[LegacyStack]) -> bool {\n    let ids: HashSet<String> = stacks.iter().map(|s| s.stack_id.clone()).collect();\n    heads.iter().all(|h| ids.contains(&h.stack_id))\n}","typeGuard":"fn legacy_metadata_is_consistent(\n    heads: &[HeadEntry],\n    stack_ids: &std::collections::HashSet<String>,\n) -> bool {\n    heads.iter().all(|h| stack_ids.contains(&h.stack_id))\n}","tryCatchPattern":"match migrate_legacy_storage(&vb_json) {\n    Ok(v2) => Ok(v2),\n    Err(e) if e.to_string().contains(\"Missing stack\") => {\n        // quarantine the file, restore from backup/snapshot, then retry\n        Err(e)\n    },\n    Err(e) => Err(e),\n}","preventionTips":["Validate head->stack referential integrity before running the legacy migration.","Keep metadata backups/snapshots so an inconsistent file can be restored instead of hand-repaired.","Never hand-edit virtual_branches.json; let the app rewrite it atomically."],"tags":["metadata","legacy-migration","virtual-branches","data-corruption"],"backgroundTag":"corrupt-metadata-file","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}