gitbutlerapp/gitbutler · error · anyhow::Error

The conflict in "{}" cannot be resolved automatically: {} Re

Error message

The conflict in "{}" cannot be resolved automatically: {} Resolve this commit in edit mode instead.

What it means

resolve_commit_conflicts_ai() (via resolve_commit_conflicts_with) promises a FULLY resolved commit in one shot, so any file in the request's `manual` list - conflicts with no marker block to splice a resolution into: side deletions, non-blob entries, binary or oversized files - is a hard stop before the model is even called. Only the first manual file is reported; the message points to edit mode. This differs from resolve_commit_conflict_hunks(), which resolves what it is asked to and deliberately leaves the rest conflicted.

Source

Thrown at crates/but-api/src/resolve/mod.rs:555

/// malformed model output) or if its response fails validation against the
/// request.
pub fn resolve_commit_conflicts_with(
    ctx: &mut but_ctx::Context,
    commit_id: gix::ObjectId,
    dry_run: DryRun,
    resolve: impl Fn(&ResolutionRequest) -> anyhow::Result<ResolutionResponse>,
) -> anyhow::Result<AiResolutionResult> {
    let request = {
        let _guard = ctx.shared_worktree_access();
        let repo = ctx.repo.get()?;
        context::build_request(&repo, commit_id)?
    };
    // This path promises a fully resolved commit, so a file the model cannot be
    // shown is a hard stop rather than something to leave behind — unlike
    // `resolve_commit_conflict_hunks()`, which resolves what it is asked to and
    // leaves the rest conflicted by design.
    if let Some(file) = request.manual.first() {
        bail!(
            "The conflict in \"{}\" cannot be resolved automatically: {} Resolve this commit in edit mode instead.",
            file.path,
            file.reason
        );
    }

    // The model call happens without any worktree lock; the request is plain
    // data and the apply step below re-reads the workspace under the exclusive
    // lock, so it fails closed if the commit changed in the meantime.
    let ((picks, files), summary) = retry_once(|| {
        let response = resolve(&request)?;
        let validated = validate_ai_response(&request, &response)?;
        Ok((validated, response.summary))
    })?;

    let mut guard = ctx.exclusive_worktree_access();
    // The workspace graph may have been cached before or during the model
    // call; the commit-presence check in apply must run against the state

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Pre-check with commit_conflicts(commit_id): a non-empty `manual` list means AI full-resolve will refuse
  2. Resolve the manual file(s) yourself in edit mode, then optionally AI-resolve the remainder
  3. Or use resolve_commit_conflict_hunks for the hunk-addressable conflicts - it leaves manual conflicts behind by design and reports them in its result

Example fix

// before
await api.resolveCommitConflictsAi(commitId); // throws on the first binary/deletion conflict

// after
const c = await api.commitConflicts(commitId);
if (c.manual.length > 0) {
  showManualResolution(c.manual); // edit mode for binaries/deletions
} else {
  await api.resolveCommitConflictsAi(commitId);
}
Defensive patterns

Strategy: validation

Validate before calling

// Gate the AI full-resolve on manual being empty
const c = await api.commitConflicts(commitId);
if (c.manual.length > 0) {
  routeManualFilesToEditMode(c.manual);
} else {
  await api.resolveCommitConflictsAi(commitId);
}

Try / catch

try {
  await api.resolveCommitConflictsAi(commitId);
} catch (err) {
  if (String(err).includes('cannot be resolved automatically')) {
    openEditModeFor(commitId); // manual files present - AI path will never succeed
  } else throw err;
}

Prevention

When it happens

Trigger: Calling resolve_commit_conflicts_ai on a commit whose conflict set includes a file deleted on one side, a binary (image, font, some lockfiles), a submodule, or an oversized file. The request builder sorts those into `manual`, and the first one aborts the AI path.

Common situations: Merge conflicts involving assets/binaries; delete-vs-edit conflicts; submodules; very large generated files; users clicking 'Resolve with AI' expecting it to also handle structural conflicts.

Related errors


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