tinyhumansai/openhuman · error

Failed to read Exa response JSON: {e}

Error message

Failed to read Exa response JSON: {e}

What it means

Raised in ExaClient::post when reading the successful response body as JSON fails (resp.json() errors) — i.e. Exa answered with a non-JSON or truncated body. Note the two-step failure handling: non-2xx statuses are bailed earlier with a status-specific message (401/403 name the API key and deliberately do not echo Exa's error body into the transcript, since it echoes the query back), so this error means an HTTP-successful but unparseable response.

Source

Thrown at src/openhuman/search/tools/exa.rs:248

        if !status.is_success() {
            // Read and drop the body: Exa echoes the query back in errors and
            // the message reaches the agent transcript.
            let body_len = resp.text().await.unwrap_or_default().len();
            tracing::warn!(status = %status, body_len, "[exa] non-2xx response");
            if status == reqwest::StatusCode::UNAUTHORIZED
                || status == reqwest::StatusCode::FORBIDDEN
            {
                anyhow::bail!(
                    "Exa rejected the configured API key (HTTP {status}). \
                     Check your Exa API key under Connections > Search engine."
                );
            }
            anyhow::bail!("Exa returned non-2xx status {status}");
        }

        resp.json().await.map_err(|e| {
            tracing::warn!("[exa] failed to read response JSON: {e}");
            anyhow::anyhow!("Failed to read Exa response JSON: {e}")
        })
    }

    async fn post_documents(&self, path: &str, body: Value) -> anyhow::Result<Vec<ExaResultItem>> {
        let value = self.post(path, body).await?;
        let parsed: ExaSearchResponse = serde_json::from_value(value).map_err(|e| {
            tracing::warn!("[exa] failed to parse {path} response: {e}");
            anyhow::anyhow!("Failed to parse Exa response: {e}")
        })?;
        tracing::debug!(path, result_count = parsed.results.len(), "[exa] call ok");
        Ok(parsed.results)
    }

    fn render_plain(&self, results: &[ExaResultItem], heading: &str, limit: usize) -> String {
        if results.is_empty() {
            return format!("No Exa results for: {heading}");
        }

View on GitHub (pinned to 7491200858)

Solutions

  1. Retry the request — a truncated or HTML error page (e.g. from an intercepting proxy) is often transient.
  2. Check whether a proxy or captive portal is replacing the JSON body with HTML.
  3. Verify the configured api_url points at the Exa API and not an intermediate gateway.
  4. If persistent, capture body length in logs (already warned) and report to Exa support.
Defensive patterns

Strategy: retry

When it happens

Trigger: Thrown at src/openhuman/search/tools/exa.rs:248 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/1d32942c9a568820. Report an issue: GitHub.