gitbutlerapp/gitbutler · error · but_error::Code

Validation

Validation

Error message

Commit {commit_id} is not conflicted

What it means

but-api's conflict-resolution request builder refuses commits that carry no conflict data: but_core::Commit::conflicted_tree_ids() returned None, so the commit has no base/ours/theirs tree triple and is not one of GitButler's conflicted workspace commits. The error carries the Validation code — the request itself was inconsistent, not an IO or git failure. Note the doc comment: conflicts that exist but cannot be spliced (side deletions, binary, oversized files) go to 'manual' instead; only fully non-conflicted commits fail here.

Source

Thrown at crates/but-api/src/resolve/context.rs:134

}

#[cfg(feature = "export-schema")]
but_schemars::register_sdk_type!(ManualConflict);

/// Re-merge the conflict trees of `commit_id` and extract all conflict hunks.
///
/// Conflicts with no marker block to splice a resolution into — side deletions,
/// non-blob entries, binary or oversized files — are reported in `manual`
/// rather than failing the request, so the rest of the commit stays workable.
pub fn build_request(
    repo: &gix::Repository,
    commit_id: gix::ObjectId,
) -> anyhow::Result<ResolutionRequest> {
    use gix::prelude::ObjectIdExt as _;

    let commit = but_core::Commit::from_id(commit_id.attach(repo))?;
    let Some((base, ours, theirs)) = commit.conflicted_tree_ids()? else {
        bail!(
            anyhow::anyhow!(Code::Validation)
                .context(format!("Commit {commit_id} is not conflicted"))
        );
    };

    let commit_message = but_core::commit::strip_conflict_markers(commit.message.as_ref())
        .to_str_lossy()
        .into_owned();
    let parent_message = commit
        .parents
        .first()
        .and_then(|parent_id| but_core::Commit::from_id(parent_id.attach(repo)).ok())
        .map(|parent| commit_title(&parent));

    let (base, ours, theirs) = (base.detach(), ours.detach(), theirs.detach());
    let repo = repo.clone().for_tree_diffing()?;
    // Merge without favoring a side to reproduce the actual conflicts, and
    // force diff3-style markers with the sentinel labels so every hunk carries

View on GitHub (pinned to 2497b8007a)

Solutions

  1. Verify conflicted-ness first: re-read the commit and require conflicted_tree_ids() to be Some.
  2. Refresh the commit id from current workspace state instead of reusing a cached one.
  3. If conflicts were already resolved, route to the normal apply flow rather than resolution.
  4. Map Code::Validation to a user-facing 'nothing to resolve' response instead of a hard error.

Example fix

// before
let request = build_request(&repo, commit_id)?;

// after
let commit = but_core::Commit::from_id(commit_id.attach(&repo))?;
anyhow::ensure!(
    commit.conflicted_tree_ids()?.is_some(),
    "commit {commit_id} has no conflicts to resolve"
);
let request = build_request(&repo, commit_id)?;
Defensive patterns

Strategy: validation

Validate before calling

let commit = but_core::Commit::from_id(commit_id.attach(&repo))?;
if commit.conflicted_tree_ids()?.is_none() {
    anyhow::bail!("commit {commit_id} has no conflicts to resolve");
}

Try / catch

match build_request(&repo, commit_id) {
    Err(err) if err.chain().any(|c| c.to_string().contains("is not conflicted")) => {
        refresh_workspace_state(&ctx)?; // id may be stale
        build_request(&repo, refreshed_commit_id)
    }
    r => r,
}?

Prevention

When it happens

Trigger: Calling build_request (crates/but-api/src/resolve/context.rs:118) with a plain commit, an already-resolved workspace commit, or a stale commit id — e.g. after a rebase/snapshot produced a new commit underneath the caller.

Common situations: UI retrying the resolve flow after the user already saved resolutions; commit ids cached from a previous workspace state; scripts driving the resolve API against arbitrary SHAs.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17). Data as JSON: /api/errors/5b82c1be647c7a22. Report an issue: GitHub.