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 stateView on GitHub (pinned to caf1f223d3)
Solutions
- Pre-check with commit_conflicts(commit_id): a non-empty `manual` list means AI full-resolve will refuse
- Resolve the manual file(s) yourself in edit mode, then optionally AI-resolve the remainder
- 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
- Surface the manual list from commit_conflicts (or HunkResolutionResult.manual) in the UI so users see binaries/deletions before clicking AI resolve
- Remember only the FIRST manual file is reported - fix or pre-check all of them
- For commits with manual conflicts, use per-hunk resolve for the rest instead of the all-or-nothing AI call
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
- Conflict {} of "{}" was addressed more than once
- "{}" has {} conflict{}, but conflict {} was addressed
- The resolution for "{path}" contains a conflict marker ({mar
- Validation
- Re-merging the conflicting trees of commit {commit_id} yield
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/571c5354d125490c.
Report an issue: GitHub.