tonhowtf/omniget · error

LibreTranslate: resposta invalida ({})

Error message

LibreTranslate: resposta invalida ({})

What it means

This error is thrown by translate_batch_libre when the HTTP response body returned by a LibreTranslate server cannot be parsed as JSON. The library uses anyhow! to wrap the serde_json parse error, so the original deserialization message appears inside the parentheses. It indicates the server replied with something other than the expected JSON payload (e.g. HTML, empty body, or a proxy error page).

Solutions

  1. Verify the endpoint URL points at the JSON API route (e.g. http://host:5000/translate), not the web UI root
  2. Open the endpoint with curl -X POST -H 'Content-Type: application/json' -d '{...}' to inspect the raw body and confirm it is JSON
  3. Check any reverse proxy in front of LibreTranslate; ensure it passes the request through instead of serving HTML error pages
  4. Increase client timeout/limits or retry, in case the body was truncated mid-transfer
  5. Check LibreTranslate server logs for crashes (OOM kill, API key requirement redirect) and restart/upgrade it

Example fix

// before: any non-JSON body falls through to json() and fails opaquely
let v: serde_json::Value = resp.json().await
    .map_err(|e| anyhow!("LibreTranslate: resposta invalida ({})", e))?;
// after: capture status and raw body for a diagnosable error
let status = resp.status();
let raw = resp.text().await.map_err(|e| anyhow!("LibreTranslate: leitura falhou ({})", e))?;
let v: serde_json::Value = serde_json::from_str(&raw)
    .with_context(|| format!("LibreTranslate: resposta invalida (status {}, corpo: {:.200})", status, raw))?;
Defensive patterns

Strategy: try-catch

Validate before calling

// Probe the endpoint before translating
let health = client.post(&url).json(&serde_json::json!({
    "q": "hi", "source": "en", "target": "pt"
})).send().await?;
let ct_ok = health.headers().get("content-type")
    .map(|v| v.to_str().unwrap_or("").contains("json")).unwrap_or(false);
if !ct_ok { return Err(anyhow!("endpoint nao retorna JSON: verifique a URL do LibreTranslate")); }

Type guard

fn is_json_response(resp: &reqwest::Response) -> bool {
    resp.headers().get(reqwest::header::CONTENT_TYPE)
        .and_then(|v| v.to_str().ok())
        .map(|ct| ct.contains("application/json"))
        .unwrap_or(false)
}

Try / catch

match translate_cues(...).await {
    Ok(cues) => cues,
    Err(e) if e.to_string().contains("resposta invalida") => {
        eprintln!("Resposta nao-JSON do LibreTranslate; verifique URL/proxy: {e}");
        fallback_translate(cues)?
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling translate_cues with a LibreTranslate endpoint whose response body is not valid JSON: server down and returning an HTML error page, a reverse proxy (nginx/Cloudflare) intercepting the request, a wrong URL path hitting a non-API route, or a truncated/gzip response the client cannot decode.

Common situations: Self-hosted LibreTranslate behind a misconfigured reverse proxy; pointing the endpoint at the web UI URL instead of /translate; API gateway returning 502/503 HTML pages; LibreTranslate shutting down mid-request due to OOM; corporate proxy injecting an HTML block page.

Understand the failure class

Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.

Related errors


AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12). Data as JSON: /api/errors/4e09321b39e5eefb. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/core/tools/srt_translate.rs:144

    opts: &TranslateOptions,
) -> anyhow::Result<Vec<Option<String>>> {
    let client = super::client()?;
    let url = format!("{}/translate", base_url.trim_end_matches('/'));
    let mut body = serde_json::json!({
        "q": lines,
        "source": if opts.source_lang.is_empty() { "auto" } else { opts.source_lang.as_str() },
        "target": opts.target_lang,
        "format": "text",
    });
    if !api_key.is_empty() {
        body["api_key"] = serde_json::Value::String(api_key.to_string());
    }
    let resp = client.post(&url).json(&body).send().await?;
    let status = resp.status();
    let v: serde_json::Value = resp
        .json()
        .await
        .map_err(|e| anyhow!("LibreTranslate: resposta invalida ({})", e))?;
    if !status.is_success() {
        return Err(anyhow!(
            "LibreTranslate: HTTP {} {}",
            status.as_u16(),
            v["error"].as_str().unwrap_or("")
        ));
    }
    let out = match &v["translatedText"] {
        serde_json::Value::Array(a) => a
            .iter()
            .map(|x| x.as_str().map(|s| s.to_string()))
            .collect(),
        serde_json::Value::String(s) => vec![Some(s.clone())],
        _ => vec![None; lines.len()],
    };
    Ok(out)
}

View on GitHub (pinned to 8600b91f42)