tonhowtf/omniget · error · anyhow::Error
o servidor respondeu algo que não é JSON
Error message
o servidor respondeu algo que não é JSON ({}): {} What it means
get_json() strips Medium's anti-hijack JSON prefix (])}while(1);</x>) then parses with serde_json. If parsing still fails, the body was not the expected JSON (HTML error page, empty body, login redirect page), and it reports the serde error plus the URL.
Solutions
- Re-capture medium.com cookies — an HTML login page usually means the session expired
- Open the URL in the error in a browser to see what the server actually returns
- Retry later if the site is serving a transient error page
- Update parsing if Medium changed its JSON envelope; check strip_json_prefix handling
Defensive patterns
Strategy: try-catch
Try / catch
match fetcher.get_json(url).await {
Err(e) if e.to_string().contains("não é JSON") => {
// likely HTML login/error page: refresh session then retry
refresh_cookies();
return fetcher.get_json(url).await;
}
other => other,
} Prevention
- Detect HTML responses (starts with '<') and treat them as auth failures
- Keep strip_json_prefix tested against Medium's current prefix
- Re-capture cookies when endpoints start returning HTML pages
When it happens
Trigger: A GET via get_json returns HTTP 200 but the body after strip_json_prefix is not valid JSON — e.g. serde_json::from_str fails — thrown to all callers (from_session, discover, export_publication, fetch_post, expand).
Common situations: Expired session causing Medium to return an HTML login/error page with 200 status; a captcha or consent page; endpoint changed its response format; truncated response body from a proxy.
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
- invalid JSON
- No valid cookies found in file (expected Netscape format)
- Failed to parse m3u8: neither master nor media playlist
- Expected a JSON array of cookie objects.
- Missing 'format' field
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/889c3b3f2b4d493b.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/core/tools/blogs/mod.rs:204
}
Ok(r) => return Err(anyhow!("HTTP {} em {}", r.status(), url)),
Err(e) if attempt < TRIES => {
tokio::time::sleep(wait).await;
wait *= 2;
let _ = e;
}
Err(e) => return Err(e.into()),
}
}
Err(anyhow!("não foi possível ler {}", url))
}
/// GET com JSON de volta. Tolera o prefixo anti-sequestro do Medium.
pub async fn get_json(&self, url: &str) -> Result<serde_json::Value> {
let text = self.get_text(url).await?;
let body = strip_json_prefix(&text);
serde_json::from_str(body)
.map_err(|e| anyhow!("o servidor respondeu algo que não é JSON ({}): {}", e, url))
}
}
/// O Medium serve JSON prefixado com `])}while(1);</x>` para que ninguém
/// consiga incluir a resposta como `<script>`. É lixo antes do primeiro `{`
/// ou `[`; cortar é obrigatório antes de parsear.
pub fn strip_json_prefix(text: &str) -> &str {
let t = text.trim_start();
if t.starts_with('{') || t.starts_with('[') {
return t;
}
match t.find(['{', '[']) {
Some(i) => &t[i..],
None => t,
}
}
// ── HTML → Markdown ────────────────────────────────────────────────────View on GitHub (pinned to 8600b91f42)