gitbutlerapp/gitbutler · error · anyhow::Error

The model returned {} resolved hunks for "{}" but the file h

Error message

The model returned {} resolved hunks for "{}" but the file has {} conflicts

What it means

For each resolved file the model must return exactly one resolved hunk per conflict in that file - full coverage per file, not a subset. A count mismatch (merged two conflicts into one entry, skipped one, or invented extra) fails validation; the message reports both counts so the drift is visible. The call is retried once automatically.

Source

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

    let mut picks: apply::PicksPerFile = vec![BTreeMap::new(); request.files.len()];
    let mut resolved_files: Vec<Option<ResolvedFile>> = vec![None; request.files.len()];

    for resolution in &response.resolutions {
        let Some(&index) = files_by_path.get(&apply::normalize_path(&resolution.path)) else {
            bail!(
                "The model returned a resolution for \"{}\", which was not requested",
                resolution.path
            );
        };
        if resolved_files[index].is_some() {
            bail!(
                "The model returned more than one resolution for \"{}\"",
                resolution.path
            );
        }
        let file = &request.files[index];
        if resolution.hunks.len() != file.hunks.len() {
            bail!(
                "The model returned {} resolved hunks for \"{}\" but the file has {} conflicts",
                resolution.hunks.len(),
                file.path,
                file.hunks.len()
            );
        }
        if resolution.reasoning.trim().is_empty() {
            bail!("The model returned no reasoning for \"{}\"", file.path);
        }
        for (hunk_index, hunk) in resolution.hunks.iter().enumerate() {
            apply::ensure_no_markers(&hunk.resolved_content, &file.path)?;
            picks[index].insert(
                hunk_index,
                apply::HunkPick::Content(hunk.resolved_content.clone()),
            );
        }
        resolved_files[index] = Some(ResolvedFile {
            path: file.path.clone(),

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Trust the automatic retry - a fresh sample often lands the correct count
  2. Move to a stronger model for files with many conflicts
  3. If it persists, resolve that file with explicit per-hunk specs (ours/theirs/content) via resolve_commit_conflict_hunks
Defensive patterns

Strategy: retry

Try / catch

try {
  await api.resolveCommitConflictsAi(commitId);
} catch (err) {
  const m = String(err).match(/returned (\d+) resolved hunks .* but the file has (\d+)/);
  if (m) {
    await api.resolveCommitConflictsAi(commitId); // count drift - resample once
  } else throw err;
}

Prevention

When it happens

Trigger: Model collapses adjacent conflicts into a single resolution object, omits the 'easy' conflict, or emits hunks per a different mental model of the file. Anything other than resolution.hunks.len() == file.hunks.len() bails.

Common situations: Files with many conflicts where the model loses count; nested/adjacent marker blocks confusing the split; models that resolve the file wholesale in one hunk.

Related errors


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