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

  1. Inspect the commit's tree: `git ls-tree <commit>` and check which of the numbered side/base/ConflictFiles entries actually exist.
  2. 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.
  3. 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.
  4. 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

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

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/79792674d8c036d6. Report an issue: GitHub.