Hmbown/CodeWhale · error
Model response incomplete: provider stop reason
Error message
Model response incomplete: provider stop reason `{reason}`; no complete response or tool call was accepted. What it means
Turn failure error built in `Engine::run_turn` (crates/tui/src/core/engine/turn_loop.rs:1766): the provider ended the stream with a stop reason that produced neither a complete assistant response nor an accepted tool call, so the partial visible text is recorded as interrupted and the turn is marked Failed. It guards against silently presenting truncated model output as a finished answer.
Solutions
- Raise the model's max output tokens in the model config if the stop reason is `length`, then resend the message.
- Rephrase or reduce the prompt if a content filter likely truncated it; check the provider's safety-rejection details.
- Retry the request — transient provider truncation usually succeeds on a retry; the engine's transparent retry (#103) may already cover repeated attempts.
- Read the logged warning (`crate::logging::warn`) for the exact stop reason to pick between the length/filter/transport fixes.
Example fix
// before: tokens exhausted mid-answer "model.max_output_tokens": 256 // after: allow the response to complete "model.max_output_tokens": 4096
Defensive patterns
Strategy: retry
Validate before calling
// before sending, ensure the request allows a complete response
assert!(request.max_output_tokens.is_none() || request.max_output_tokens.unwrap() >= 1024,
"max_output_tokens too small; response will truncate"); Try / catch
match turn_outcome.status {
TurnOutcomeStatus::Failed if outcome_error.contains("no complete response or tool call was accepted") => {
// inspect provider stop reason from the log, adjust max tokens/prompt, retry once
}
_ => {}
} Prevention
- Set generous max-output-token limits for open-ended prompts.
- Avoid prompts likely to trip provider content filters in agentic turns.
- Enable/keep the engine's transparent retry for transient provider truncation.
- Log stop reasons and alert on repeated `length` stops per model config.
When it happens
Trigger: Provider returns a stop reason like `length` (max tokens hit before any complete message/tool call was accepted), `content_filter`, or an abnormal stop right after stream start; retries/tool-call boundaries in the inner loop accepted nothing before the stream ended.
Common situations: Very low max-output-token settings on the model config; safety filters firing early on sensitive prompts; provider instability truncating streams near the start; tool-call arguments cut off mid-stream so the call is not accepted.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- Model returned terminal stop reason
- Antigravity cloud-code is stream-only; blocking…
- Model stream ended with no answer or tool call.
- stream_duration_limit
- stream_overflow
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/755670ccf540deb2.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/core/engine/turn_loop.rs:1766
} else {
for tool in &tool_uses {
let _ = self
.tx_event
.send(Event::ToolCallComplete {
id: tool.id.clone(),
name: tool.name.clone(),
result: Ok(incomplete_tool_result(reason)),
})
.await;
}
// Do not emit MessageComplete: hosts must retain the visible
// fragment as interrupted/failed rather than recording it as
// a completed assistant item.
self.add_interrupted_assistant_text(¤t_text_visible)
.await;
let error = format!(
"Model response incomplete: provider stop reason `{reason}`; no complete response or tool call was accepted."
);
crate::logging::warn(&error);
return (TurnOutcomeStatus::Failed, Some(error));
}
}
// #103 Phase 3 — transparent retry. The inner loop above bails
// when reqwest yields chunk decode errors three times in a row;
// most of the time those are recoverable proxy / HTTP/2 issues
// and the request can simply be re-issued. Re-issue silently up
// to MAX_STREAM_RETRIES, but only when the stream produced
// nothing actionable — if any tool call landed or text was
// streamed, ship the partial state to the rest of the turn
// pipeline so we don't double-bill the user by re-running it.
// The post-content exceptions to that rule are the #2990
// sleep-resume and the mid-stream network-drop resumes: those
// discard the uncommitted fragment unless an operator watched
// visible text land (see `StreamResume::InteractiveNetworkDrop`).
//View on GitHub (pinned to 73e0f67d83)