gitbutlerapp/gitbutler · error · anyhow::Error
There has been a malformed conflicted commit, unable to find
Error message
There has been a malformed conflicted commit, unable to find the conflicted files
What it means
`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.
Source
Thrown at crates/but-core/src/commit/mod.rs:713
}
}
/// Conflict specific details
impl Commit<'_> {
/// Obtains the conflict entries of a conflicted commit if the commit is
/// conflicted, otherwise returns None.
pub fn conflict_entries(&self) -> anyhow::Result<Option<ConflictEntries>> {
let repo = self.id.repo;
if !self.is_conflicted() {
return Ok(None);
}
let tree = repo.find_tree(self.tree)?;
let Some(conflicted_entries_blob) =
tree.find_entry(TreeKind::ConflictFiles.as_tree_entry_name())
else {
bail!(
"There has been a malformed conflicted commit, unable to find the conflicted files"
);
};
let conflicted_entries_blob = conflicted_entries_blob.object()?.into_blob();
let conflicted_entries: ConflictEntries =
toml::from_str(&conflicted_entries_blob.data.as_bstr().to_str_lossy())?;
Ok(Some(conflicted_entries))
}
}
/// Represents what was causing a particular commit to conflict when rebased.
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
#[cfg_attr(feature = "export-schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "camelCase")]
pub struct ConflictEntries {
/// The ancestors that were conflicted
pub ancestor_entries: Vec<PathBuf>,View on GitHub (pinned to caf1f223d3)
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.
Defensive patterns
Strategy: validation
Validate before calling
// Rust — check the ConflictFiles entry exists before parsing conflict entries
fn has_conflict_entries(commit: &but_core::Commit<'_>) -> anyhow::Result<bool> {
if !commit.is_conflicted() {
return Ok(false);
}
let repo = commit.id.repo;
let tree = repo.find_tree(commit.tree)?;
Ok(tree
.find_entry(but_core::commit::TreeKind::ConflictFiles.as_tree_entry_name())
.is_some())
} Try / catch
// Distinguish 'commit not conflicted' (Ok(None)) from 'malformed conflicted commit' (Err)
match commit.conflict_entries() {
Ok(Some(entries)) => { /* render conflicts */ }
Ok(None) => { /* not conflicted */ }
Err(err) if err.to_string().contains("malformed conflicted commit") => {
tracing::warn!("conflict metadata missing on {}; offering re-resolve", commit.id);
// offer the user conflict resolution / undo instead of failing the workflow
}
Err(err) => return Err(err),
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- in commit {}, number of sides ({}) is not exactly one more t
- '{other}' is not a conflicted uncommitted file; `but resolve
- In resolution mode, edit the files and run `but resolve fini
- Failed to communicate with LM Studio server: ${error instanc
- Invalid response: ${JSON.stringify(result)}
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/79792674d8c036d6.
Report an issue: GitHub.