Hmbown/CodeWhale · error · anyhow::Error

translate: provider response did not contain text content

Error message

translate: provider response did not contain text content

What it means

For OpenAI-compatible (chat completions) translation endpoints, the client reads `choices[0].message.content`; an empty/whitespace-only content string is rejected with this error instead of returning an empty translation. It is the OpenAI-format counterpart of the Anthropic empty-text check.

Solutions

  1. Retry the translation; content-filter or transient empty replies often clear on retry.
  2. Rephrase or shorten the input text if a content filter is emptying the response.
  3. Use a different model for translation that returns plain text content.
  4. Verify the endpoint is an OpenAI-compatible chat-completions API and not a proxy altering the payload.
Defensive patterns

Strategy: try-catch

Try / catch

match result {
    Err(e) if e.to_string().contains("did not contain text content") => {
        log::warn!("empty translation content; retrying once");
        result = translate(input).await;
    }
    r => r?,
}

Prevention

When it happens

Trigger: Provider returns choices[0].message.content = "" or whitespace — e.g. the model refused, hit a content filter, or returned a reasoning-only response with empty content.

Common situations: Content-filter refusals on the input text; a reasoning model returning empty content; misconfigured endpoint behind a proxy that strips content.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/ecb6f12b5bb95755. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/client.rs:2900

                .iter()
                .any(|field| usage.contains_key(*field))
            });
        let usage = parse_usage(value.get("usage"));
        let stop_reason = value["choices"][0]["finish_reason"].as_str();
        let usage = (usage_reported && usage != Usage::default()).then_some(usage);
        let translated = if codewhale_models::is_incomplete_stop_reason(stop_reason) {
            Err(anyhow::anyhow!(
                "translate: provider response incomplete ({})",
                codewhale_models::stop_reason_detail(stop_reason)
            ))
        } else {
            value["choices"][0]["message"]["content"]
                .as_str()
                .ok_or_else(|| anyhow::anyhow!("translate: unexpected API response shape"))
                .and_then(|translated| {
                    let translated = translated.trim().to_string();
                    if translated.is_empty() {
                        bail!("translate: provider response did not contain text content");
                    }
                    Ok(translated)
                })
        };

        Ok(TranslationProviderResponse {
            translated,
            route,
            usage,
        })
    }

    /// Test adapter for asserting the translated text; production retains receipts.
    #[cfg(test)]
    async fn translate(&self, text: &str, model: &str, target_language: &str) -> Result<String> {
        self.translate_with_usage(text, model, target_language)
            .await?
            .translated

View on GitHub (pinned to 73e0f67d83)