{"record":{"id":"edb093fa6d2d8590","repo":"Hmbown/CodeWhale","slug":"translate-provider-response-incomplete","errorCode":null,"errorMessage":"translate: provider response incomplete ({})","messagePattern":"translate: provider response incomplete \\((.+?)\\)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/client.rs","lineNumber":2815,"sourceCode":"            // Non-Chat dialects reuse the prepared-request seam so translation\n            // cannot drift from production shaping. Translation is still an\n            // *auxiliary* call, not a primary agent turn: the Chat dialect\n            // below builds its own small fixed body, and `/preview-request`\n            // deliberately does not claim to describe either\n            // (see `docs/PREVIEW_REQUEST.md`).\n            let prepared = self.prepare_outbound_request(\n                translation_message_request(text, model, target_language, max_tokens),\n                false,\n            )?;\n            let response = match prepared.dialect {\n                WireDialect::OpenAiResponses => self.handle_responses_message(&prepared).await?,\n                WireDialect::AnthropicMessages => self.handle_anthropic_message(&prepared).await?,\n                WireDialect::ChatCompletions => unreachable!(),\n            };\n            let usage = (response.usage != Usage::default()).then_some(response.usage.clone());\n            let translated =\n                if codewhale_models::is_incomplete_stop_reason(response.stop_reason.as_deref()) {\n                    Err(anyhow::anyhow!(\n                        \"translate: provider response incomplete ({})\",\n                        codewhale_models::stop_reason_detail(response.stop_reason.as_deref())\n                    ))\n                } else {\n                    translation_text_from_response(&response)\n                };\n            return Ok(TranslationProviderResponse {\n                translated,\n                route,\n                usage,\n            });\n        }\n\n        let url = api_url_with_suffix(\n            self.chat_transport_base_url(),\n            \"chat/completions\",\n            self.path_suffix.as_deref(),\n        );","sourceCodeStart":2797,"sourceCodeEnd":2833,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/client.rs#L2797-L2833","documentation":"After receiving an Anthropic Messages (or Responses API) reply, the client checks the stop reason with codewhale_models::is_incomplete_stop_reason and errors out if the provider explicitly did not finish: output token limit (length/max_tokens), content_filter, model_context_window_exceeded, or any `incomplete:*` reason. Rather than returning a truncated translation as if it were complete, the client surfaces the provider's stop reason detail in the message.","triggerScenarios":"A request whose response carries stop_reason \"length\"/\"max_tokens\"/\"max_output_tokens\" (output cap hit), \"content_filter\", \"model_context_window_exceeded\", or an `incomplete:`-prefixed reason, on the Anthropic Messages path in the translate flow (client.rs:2815).","commonSituations":"Low max_tokens / output budget configured while the model needs more tokens; a provider safety filter truncating output; the conversation exceeding the model context window so generation stops early; a provider introducing a new incomplete stop reason spelled `incomplete:<future-reason>`.","solutions":["Increase the max output tokens / output budget for the request so generation can finish","Shrink the prompt or conversation history (compact context) to avoid context-window exhaustion","Check whether a content filter applies and rephrase or switch model/provider","Read the detail inside the message (stop_reason_detail) to identify which of length/content_filter/context-window caused it and address that specific cause"],"exampleFix":"// before\nrequest.max_tokens = 256; // generation truncated at limit\n\n// after\nrequest.max_tokens = 4096; // enough headroom for the full response","handlingStrategy":"retry","validationCode":"// Before sending, check the request fits the output budget:\nif request.max_tokens.unwrap_or(0) < 1024 {\n    eprintln!(\"max_tokens is low; Anthropic responses risk finishing with stop_reason=\\\"max_tokens\\\"\");\n}\n","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().starts_with(\"translate: provider response incomplete\") => {\n        let detail = e.to_string(); // e.g. \"max_tokens\" / \"content_filter\"\n        if detail.contains(\"max_tokens\") || detail.contains(\"length\") {\n            // retry once with a larger output budget and compacted context\n        } else {\n            // surface to user: filter or context-window issue, do not blind-retry\n        }\n    }\n    other => other?,\n}\n","preventionTips":["Set generous output token budgets relative to expected answer length","Compact conversation history before it approaches the model context window","Never treat a partial response as final; check stop_reason in any raw Anthropic handling","Subscribe to provider changelogs for new stop reasons; `incomplete:*` reasons are intentionally rejected"],"tags":["api","anthropic","truncated-response","stop-reason"],"backgroundTag":"upstream-api-error","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}