gitbutlerapp/gitbutler · error · anyhow::Error
The model returned no reasoning for "{}"
Error message
The model returned no reasoning for "{}" What it means
Every per-file resolution from the model must include non-empty `reasoning` (trimmed). GitButler requires the model to justify each resolution so it can be surfaced to the user; an empty or whitespace-only reasoning string fails the whole response. This is a contract of ResolutionResponse, not a nicety.
Source
Thrown at crates/but-api/src/resolve/mod.rs:640
);
};
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(),
hunks: resolution
.hunks
.iter()
.map(|hunk| hunk.resolved_content.clone())
.collect(),
reasoning: resolution.reasoning.clone(),
});
}View on GitHub (pinned to caf1f223d3)
Solutions
- Let the single automatic retry run - second samples usually include reasoning
- Use a model/provider combination that reliably fills structured text fields
- If you inject resolutions via resolve_commit_conflicts_with (tests, custom frontends), always populate a non-empty reasoning per file
Example fix
// before (custom resolver injected into resolve_commit_conflicts_with)
return { resolutions: files.map(f => ({ path: f.path, hunks: [...], reasoning: ' ' })), summary: '' };
// after
return { resolutions: files.map(f => ({ path: f.path, hunks: [...], reasoning: 'Took theirs: our side only deleted a comment.' })), summary: 'Resolved all files.' }; Defensive patterns
Strategy: retry
Try / catch
try {
await api.resolveCommitConflictsAi(commitId);
} catch (err) {
if (String(err).includes('no reasoning')) {
await api.resolveCommitConflictsAi(commitId); // second sample usually fills reasoning
} else throw err;
} Prevention
- When injecting resolutions yourself, always fill reasoning with a real sentence per file - empty strings are rejected
- Choose providers that enforce structured output schemas including text fields
- Do not treat reasoning as optional; it is validated like the content itself
When it happens
Trigger: The model omits the reasoning field, outputs an empty string, or fills it with whitespace/newlines only. Providers whose structured-output enforcement is lax are the usual source; small local models often skip optional-looking text fields.
Common situations: Local/small models via custom providers; schema-optional JSON modes; reasoning hidden behind a provider flag and serialized as an empty string.
Related errors
- The model returned a resolution for "{}", which was not requ
- The model returned more than one resolution for "{}"
- The model returned {} resolved hunks for "{}" but the file h
- The model returned no resolution for the conflicted file "{}
- The conflict in "{}" cannot be resolved automatically: {} Re
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/95f763c26afa8199.
Report an issue: GitHub.