{"record":{"id":"79792674d8c036d6","repo":"gitbutlerapp/gitbutler","slug":"there-has-been-a-malformed-conflicted-commit-unab","errorCode":null,"errorMessage":"There has been a malformed conflicted commit, unable to find the conflicted files","messagePattern":"There has been a malformed conflicted commit, unable to find the conflicted files","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-core/src/commit/mod.rs","lineNumber":713,"sourceCode":"    }\n}\n\n/// Conflict specific details\nimpl Commit<'_> {\n    /// Obtains the conflict entries of a conflicted commit if the commit is\n    /// conflicted, otherwise returns None.\n    pub fn conflict_entries(&self) -> anyhow::Result<Option<ConflictEntries>> {\n        let repo = self.id.repo;\n\n        if !self.is_conflicted() {\n            return Ok(None);\n        }\n\n        let tree = repo.find_tree(self.tree)?;\n        let Some(conflicted_entries_blob) =\n            tree.find_entry(TreeKind::ConflictFiles.as_tree_entry_name())\n        else {\n            bail!(\n                \"There has been a malformed conflicted commit, unable to find the conflicted files\"\n            );\n        };\n        let conflicted_entries_blob = conflicted_entries_blob.object()?.into_blob();\n        let conflicted_entries: ConflictEntries =\n            toml::from_str(&conflicted_entries_blob.data.as_bstr().to_str_lossy())?;\n\n        Ok(Some(conflicted_entries))\n    }\n}\n\n/// Represents what was causing a particular commit to conflict when rebased.\n#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]\n#[cfg_attr(feature = \"export-schema\", derive(schemars::JsonSchema))]\n#[serde(rename_all = \"camelCase\")]\npub struct ConflictEntries {\n    /// The ancestors that were conflicted\n    pub ancestor_entries: Vec<PathBuf>,","sourceCodeStart":695,"sourceCodeEnd":731,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-core/src/commit/mod.rs#L695-L731","documentation":"`Commit::conflict_entries()` first checks `is_conflicted()` (the commit is a GitButler conflict-crash commit whose message declares conflict state), then looks up the special tree entry named after `TreeKind::ConflictFiles` in the commit's tree. If that entry is absent, the commit is internally inconsistent: it claims to be conflicted but carries no conflict metadata blob, so the error bails. This is GitButler's own convention (side/base trees plus a TOML blob of conflicted entries), not a stock Git concept.","triggerScenarios":"Calling `conflict_entries()` on a commit whose message marker says 'conflicted' but whose tree lacks the ConflictFiles entry — e.g. a commit rewritten/rebased with plain git tools that dropped the special entries, a commit created by an older GitButler version with a different tree layout, or hand-crafted conflict commits.","commonSituations":"Users repairing GitButler state with raw `git rebase`/`git commit-tree` under the hood; version upgrades that changed the conflict-tree entry naming; a workspace left mid-conflict across an app update.","solutions":["Inspect the commit's tree: `git ls-tree <commit>` and check which of the numbered side/base/ConflictFiles entries actually exist.","If the conflict state is stale/unwanted, resolve or abandon the conflicted commit through GitButler (resolve conflicts or undo to before the conflict) instead of reading its entries.","If entries exist under different names (older layout), upgrade GitButler so reader and writer agree, or recreate the conflicted commit via a fresh conflict run.","Report the commit id to GitButler if the tree looks well-formed but still mismatches — this indicates a writer bug."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Rust — check the ConflictFiles entry exists before parsing conflict entries\nfn has_conflict_entries(commit: &but_core::Commit<'_>) -> anyhow::Result<bool> {\n    if !commit.is_conflicted() {\n        return Ok(false);\n    }\n    let repo = commit.id.repo;\n    let tree = repo.find_tree(commit.tree)?;\n    Ok(tree\n        .find_entry(but_core::commit::TreeKind::ConflictFiles.as_tree_entry_name())\n        .is_some())\n}","typeGuard":null,"tryCatchPattern":"// Distinguish 'commit not conflicted' (Ok(None)) from 'malformed conflicted commit' (Err)\nmatch commit.conflict_entries() {\n    Ok(Some(entries)) => { /* render conflicts */ }\n    Ok(None) => { /* not conflicted */ }\n    Err(err) if err.to_string().contains(\"malformed conflicted commit\") => {\n        tracing::warn!(\"conflict metadata missing on {}; offering re-resolve\", commit.id);\n        // offer the user conflict resolution / undo instead of failing the workflow\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Never rewrite GitButler conflict commits with raw git plumbing; use GitButler's own resolve/abort paths.","Keep GitButler version consistent across machines sharing a project directory to avoid conflict-tree layout drift.","Treat this error as a signal to re-run conflict resolution, not to parse the tree manually."],"tags":["conflict","commit-metadata","corrupt-state","gitbutler"],"backgroundTag":"malformed-commit-metadata","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}