gitbutlerapp/gitbutler · error
validated AI responses only produce content picks
Error message
validated AI responses only produce content picks
What it means
In but-api's merge-conflict AI resolution, `validate_ai_response` narrows the model's output to hunk picks, and the apply loop then asserts every pick is `HunkPick::Content`. The `unreachable!` fires if a validated pick is any other variant (e.g. a deletion), meaning the validator's contract ('only content picks survive validation') and the actual pick kinds disagree. It is a contract break between `validate_ai_response` and the consumer, not an AI-output error per se.
Source
Thrown at crates/but-api/src/resolve/mod.rs:452
.map(|&(_, hunk_index)| file.hunks[hunk_index].clone())
.collect(),
}
})
.collect(),
};
let picks = retry_once(|| {
let response = resolve(&narrowed)?;
let (picks, _files) = validate_ai_response(&narrowed, &response)?;
Ok(picks)
})?;
// The narrowed files and the validated picks are aligned index-for-index,
// and within a file the picks' 0..n keys match the sorted targets.
for (targets, file_picks) in targets_per_file.values().zip(picks) {
for (&(spec_index, _), (_, pick)) in targets.iter().zip(file_picks) {
let apply::HunkPick::Content(content) = pick else {
unreachable!("validated AI responses only produce content picks");
};
specs[spec_index].resolution = HunkResolution::Content(content);
}
}
Ok(true)
}
/// How one conflicted file was resolved, for display to the user.
#[derive(Debug, Clone, Serialize)]
#[cfg_attr(feature = "export-schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "camelCase")]
pub struct ResolvedFile {
/// The repo-relative path of the file.
pub path: String,
/// The content that replaced each conflict block, in file order.
pub hunks: Vec<String>,
/// The model's explanation of its decision for this file.
pub reasoning: String,View on GitHub (pinned to caf1f223d3)
Solutions
- Keep `validate_ai_response` rejecting every non-`Content` pick so the consumer's assertion holds.
- If deletions must be supported, replace the `unreachable!` with an explicit arm that maps the pick into `HunkResolution` or returns an error.
- Add a unit test that feeds a deletion pick through validation to pin the contract.
Example fix
// before
let apply::HunkPick::Content(content) = pick else {
unreachable!("validated AI responses only produce content picks");
};
// after
match pick {
apply::HunkPick::Content(content) => {
specs[spec_index].resolution = HunkResolution::Content(content);
}
other => {
return Err(anyhow::"validated pick was not a content pick: {other:?}"));
}
} Defensive patterns
Strategy: type-guard
Validate before calling
// pin the validator contract before applying picks
let all_content = picks.iter().flatten().all(|p| matches!(p, apply::HunkPick::Content(_)));
if !all_content {
return Ok(false); // or error: validation let a non-content pick through
} Type guard
fn content_pick(pick: &apply::HunkPick) -> Option<&apply::hunk::Content> {
match pick {
apply::HunkPick::Content(c) => Some(c),
_ => None,
}
} Prevention
- Treat validate_ai_response as the single gate: any new HunkPick variant must be rejected there first.
- Add a contract test feeding a deletion pick through validation.
- When changing pick enums, update both validator and consumer in the same commit.
When it happens
Trigger: `validate_ai_response` is extended to pass through deletion picks while the consumer still matches only `Content`; a schema/enum change adds a new `HunkPick` variant that validation does not reject; zip misalignment pairs picks with unexpected file slots.
Common situations: Evolving the resolution API to support delete-style resolutions; regenerating types for the SDK boundary; mismatches after refactoring the narrowed-file/pick index alignment.
Related errors
- BUG: {id} is queued
- candidate turns always have review or branch evidence
- we never return these as the status iteration is configured
- BUG: we use 'matching' so there are no directories
- disabled
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/f8b67c13ba8a2df0.
Report an issue: GitHub.