{"record":{"id":"755670ccf540deb2","repo":"Hmbown/CodeWhale","slug":"model-response-incomplete-provider-stop-reason-reason-no","errorCode":null,"errorMessage":"Model response incomplete: provider stop reason `{reason}`; no complete response or tool call was accepted.","messagePattern":"Model response incomplete: provider stop reason `(.+?)`; no complete response or tool call was accepted\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/core/engine/turn_loop.rs","lineNumber":1766,"sourceCode":"                } else {\n                    for tool in &tool_uses {\n                        let _ = self\n                            .tx_event\n                            .send(Event::ToolCallComplete {\n                                id: tool.id.clone(),\n                                name: tool.name.clone(),\n                                result: Ok(incomplete_tool_result(reason)),\n                            })\n                            .await;\n                    }\n                    // Do not emit MessageComplete: hosts must retain the visible\n                    // fragment as interrupted/failed rather than recording it as\n                    // a completed assistant item.\n                    self.add_interrupted_assistant_text(&current_text_visible)\n                        .await;\n                    let error = format!(\n                        \"Model response incomplete: provider stop reason `{reason}`; no complete response or tool call was accepted.\"\n                    );\n                    crate::logging::warn(&error);\n                    return (TurnOutcomeStatus::Failed, Some(error));\n                }\n            }\n\n            // #103 Phase 3 — transparent retry. The inner loop above bails\n            // when reqwest yields chunk decode errors three times in a row;\n            // most of the time those are recoverable proxy / HTTP/2 issues\n            // and the request can simply be re-issued. Re-issue silently up\n            // to MAX_STREAM_RETRIES, but only when the stream produced\n            // nothing actionable — if any tool call landed or text was\n            // streamed, ship the partial state to the rest of the turn\n            // pipeline so we don't double-bill the user by re-running it.\n            // The post-content exceptions to that rule are the #2990\n            // sleep-resume and the mid-stream network-drop resumes: those\n            // discard the uncommitted fragment unless an operator watched\n            // visible text land (see `StreamResume::InteractiveNetworkDrop`).\n            //","sourceCodeStart":1748,"sourceCodeEnd":1784,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/core/engine/turn_loop.rs#L1748-L1784","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: tokens exhausted mid-answer\n\"model.max_output_tokens\": 256\n// after: allow the response to complete\n\"model.max_output_tokens\": 4096","handlingStrategy":"retry","validationCode":"// before sending, ensure the request allows a complete response\nassert!(request.max_output_tokens.is_none() || request.max_output_tokens.unwrap() >= 1024,\n        \"max_output_tokens too small; response will truncate\");","typeGuard":null,"tryCatchPattern":"match turn_outcome.status {\n    TurnOutcomeStatus::Failed if outcome_error.contains(\"no complete response or tool call was accepted\") => {\n        // inspect provider stop reason from the log, adjust max tokens/prompt, retry once\n    }\n    _ => {}\n}","preventionTips":["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."],"tags":["llm","streaming","stop-reason","turn-engine"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}