{"record":{"id":"589cda660eab9081","repo":"gitbutlerapp/gitbutler","slug":"the-model-returned-resolved-hunks-for-but","errorCode":null,"errorMessage":"The model returned {} resolved hunks for \"{}\" but the file has {} conflicts","messagePattern":"The model returned (.+?) resolved hunks for \"(.+?)\" but the file has (.+?) conflicts","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-api/src/resolve/mod.rs","lineNumber":632,"sourceCode":"    let mut picks: apply::PicksPerFile = vec![BTreeMap::new(); request.files.len()];\n    let mut resolved_files: Vec<Option<ResolvedFile>> = vec![None; request.files.len()];\n\n    for resolution in &response.resolutions {\n        let Some(&index) = files_by_path.get(&apply::normalize_path(&resolution.path)) else {\n            bail!(\n                \"The model returned a resolution for \\\"{}\\\", which was not requested\",\n                resolution.path\n            );\n        };\n        if resolved_files[index].is_some() {\n            bail!(\n                \"The model returned more than one resolution for \\\"{}\\\"\",\n                resolution.path\n            );\n        }\n        let file = &request.files[index];\n        if resolution.hunks.len() != file.hunks.len() {\n            bail!(\n                \"The model returned {} resolved hunks for \\\"{}\\\" but the file has {} conflicts\",\n                resolution.hunks.len(),\n                file.path,\n                file.hunks.len()\n            );\n        }\n        if resolution.reasoning.trim().is_empty() {\n            bail!(\"The model returned no reasoning for \\\"{}\\\"\", file.path);\n        }\n        for (hunk_index, hunk) in resolution.hunks.iter().enumerate() {\n            apply::ensure_no_markers(&hunk.resolved_content, &file.path)?;\n            picks[index].insert(\n                hunk_index,\n                apply::HunkPick::Content(hunk.resolved_content.clone()),\n            );\n        }\n        resolved_files[index] = Some(ResolvedFile {\n            path: file.path.clone(),","sourceCodeStart":614,"sourceCodeEnd":650,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-api/src/resolve/mod.rs#L614-L650","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Trust the automatic retry - a fresh sample often lands the correct count","Move to a stronger model for files with many conflicts","If it persists, resolve that file with explicit per-hunk specs (ours/theirs/content) via resolve_commit_conflict_hunks"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await api.resolveCommitConflictsAi(commitId);\n} catch (err) {\n  const m = String(err).match(/returned (\\d+) resolved hunks .* but the file has (\\d+)/);\n  if (m) {\n    await api.resolveCommitConflictsAi(commitId); // count drift - resample once\n  } else throw err;\n}","preventionTips":["For files with many conflicts, consider pre-splitting the work via per-hunk specs instead of whole-commit AI resolve","Watch output-token limits: truncation-adjacent count drift is a capacity smell","Keep the message counts handy for logging - they tell you which file drifts"],"tags":["ai-resolution","llm-validation","hunk-count","resolve-api"],"backgroundTag":"llm-response-validation-failed","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}