Hmbown/CodeWhale · error

translate: unexpected API response shape

Error message

translate: unexpected API response shape

What it means

The ChatCompletions translation path (`translate()`) sends a minimal system+user chat request and then reads the translated text from `choices[0].message.content`, requiring it to be a JSON string. This error means the endpoint returned HTTP 200 but the body does not contain that shape — the response was still consumed by `send_json_with_retry`, so this is purely a response-shape mismatch, not a transport or status failure.

Source

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

                }
            ],
            "max_tokens": max_tokens,
            "stream": false
        });
        chat::apply_route_reasoning_controls(
            &mut body,
            self.api_provider,
            &self.base_url,
            &model,
            Some("off"),
        );

        let response = self.send_json_with_retry(&url, &body).await?;

        let value: serde_json::Value = response.json().await?;
        let translated = value["choices"][0]["message"]["content"]
            .as_str()
            .ok_or_else(|| anyhow::anyhow!("translate: unexpected API response shape"))?
            .trim()
            .to_string();

        Ok(translated)
    }

    /// List available models from the provider.
    pub async fn list_models(&self) -> Result<Vec<AvailableModel>> {
        let url = api_url(&self.base_url, "models");
        let response = self.send_with_retry(|| self.http_client.get(&url)).await?;

        let status = response.status();
        if !status.is_success() {
            let raw_error_text = bounded_error_text(response, ERROR_BODY_MAX_BYTES).await;
            let error_text = sanitize_http_error_body(
                Some(self.api_provider.display_name()),
                status.as_u16(),
                &raw_error_text,

View on GitHub (pinned to 8880682c63)

Solutions

  1. Reproduce the exact request with curl against the same base_url/model and inspect the body: `curl -s $BASE_URL/chat/completions -H "Authorization: Bearer $KEY" -d '{"model":"...","messages":[{"role":"user","content":"hi"}]}'`.
  2. Fix the base_url so it addresses an OpenAI-compatible chat-completions endpoint (correct `/v1` prefix, no trailing path duplicates).
  3. Confirm the model id actually exists on that provider (wrong ids sometimes yield 200 with an error object on gateways).
  4. If the provider legitimately returns `content` as an array of parts, switch to a provider/model that returns plain string content for translation.

Example fix

# before
base_url = "https://proxy.internal"        # returns envelope-wrapped 200
# after
base_url = "https://proxy.internal/v1"     # real chat-completions surface
Defensive patterns

Strategy: try-catch

Type guard

fn has_chat_content_shape(v: &serde_json::Value) -> bool {
    v.pointer("/choices/0/message/content").is_some_and(serde_json::Value::is_string)
}

Try / catch

let value: serde_json::Value = response.json().await?;
let translated = value
    .pointer("/choices/0/message/content")
    .and_then(serde_json::Value::as_str)
    .map(str::trim)
    .filter(|s| !s.is_empty())
    .ok_or_else(|| {
        anyhow::anyhow!("translate: unexpected API response shape: {}",
            truncate(&value.to_string(), 512))  // keep raw shape for diagnosis
    })?;

Prevention

When it happens

Trigger: Pointing the client at a provider that is not OpenAI-chat-completions-shaped (Anthropic-style `content` arrays, Google-style payloads), a gateway returning a 200 JSON error envelope, a proxy returning HTML that happens to parse as JSON, an empty `choices` array (some providers when the model filters the output), or `content` being `null`/an array instead of a string.

Common situations: Custom provider base_url pointing at the wrong endpoint version (`/v1` missing or doubled); using a model alias that routes to a non-chat completion surface; middleboxes (Cloudflare, corporate proxies) rewriting responses; providers that wrap chat responses in an envelope like `{"data": ...}`.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/e912ee0d07a6c9ce. Report an issue: GitHub.