{"record":{"id":"7ba6455d9fe61ed6","repo":"gitbutlerapp/gitbutler","slug":"snapshot-tree-id-needs-a-head-entry-if-it-has-only-a-index","errorCode":null,"errorMessage":"Snapshot tree {id} needs a 'HEAD' entry if it has only a 'index-conflicts' entry","messagePattern":"Snapshot tree (.+?) needs a 'HEAD' entry if it has only a 'index-conflicts' entry","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-core/src/snapshot/resolve_tree.rs","lineNumber":121,"sourceCode":"            (None, None) | (Some(_), None) => None,\n        };\n\n        let index = match (&head_tree, index, index_conflicts) {\n            (_, Some(index_tree), Some(index_conflicts)) => {\n                let (mut index, _path) = repo.index_from_tree(&index_tree.id())?.into_parts();\n                replace_entries_with_their_restored_conflicts(&mut index, index_conflicts.id())?;\n                Some(index)\n            }\n            (_, Some(index_tree), None) => {\n                let (index, _path) = repo.index_from_tree(&index_tree.id())?.into_parts();\n                Some(index)\n            }\n            (Some(worktree_base), None, Some(index_conflicts)) => {\n                let (mut index, _path) = repo.index_from_tree(&worktree_base.id())?.into_parts();\n                replace_entries_with_their_restored_conflicts(&mut index, index_conflicts.id())?;\n                Some(index)\n            }\n            (None, None, Some(_index_conflicts)) => bail!(\n                \"Snapshot tree {id} needs a 'HEAD' entry if it has only a 'index-conflicts' entry\",\n                id = snapshot_tree.id\n            ),\n            (_, None, None) => None,\n        };\n\n        Ok(Outcome {\n            worktree_cherry_pick,\n            index,\n            workspace_references: None,\n            metadata: None,\n        })\n    }\n\n    #[expect(clippy::indexing_slicing)]\n    fn replace_entries_with_their_restored_conflicts(\n        index: &mut gix::index::State,\n        conflict_tree: gix::Id,","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-core/src/snapshot/resolve_tree.rs#L103-L139","documentation":"resolve_tree requires a 'HEAD' entry as the base whenever 'index-conflicts' is present: conflicts are restored by replacing entries in an index built from the worktree/HEAD base. A snapshot with only 'index-conflicts' (no HEAD, no worktree) has no base index, so it bails.","triggerScenarios":"Resolving a snapshot tree containing an 'index-conflicts' entry but neither a 'HEAD' nor 'worktree' entry, so there is no index to restore conflicts into.","commonSituations":"Corrupted or partially written snapshots; snapshots edited or produced by a mismatched writer version; synthetic snapshot trees built incorrectly in tests.","solutions":["Rebuild or delete the malformed snapshot so it is regenerated with a proper 'HEAD' base","Include a 'HEAD' entry in the snapshot whenever 'index-conflicts' is written","Fix the snapshot writer to enforce the HEAD+index-conflicts invariant"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let has = |k: &str| snapshot_tree.entries.iter().any(|e| e.filename == k);\nif has(\"index-conflicts\") && !has(\"HEAD\") && !has(\"worktree\") { return Err(anyhow!(\"snapshot has index-conflicts without base\")); }","typeGuard":null,"tryCatchPattern":"match resolve_tree(repo, snapshot_tree) { Ok(t) => t, Err(_) => { clear_snapshot(repo); resolve_tree(repo, fresh_snapshot(repo)?)? } }","preventionTips":["Write a 'HEAD' base entry whenever 'index-conflicts' is recorded","Rebuild malformed snapshots instead of resolving them","Add writer-side invariant tests for snapshot entry combinations"],"tags":["rust","git","snapshot","index","corrupt-data"],"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"}