Hmbown/CodeWhale · error
Failed to parse Ollama /api/tags JSON
Error message
Failed to parse Ollama /api/tags JSON: {err} What it means
Raised when the native Ollama `GET /api/tags` response body fails `serde_json` deserialization into the OllamaTagsResponse struct. The payload is either not JSON at all (HTML error page, proxy response) or does not match the expected shape (missing `models` array, wrong field types).
Solutions
- Verify the Ollama server is reachable at the expected host/port (`curl http://localhost:11434/api/tags`)
- Log or print the raw payload to see what was actually returned
- Update the OllamaTagsResponse struct / check the Ollama version's schema
- Skip or downgrade the local catalog probe on parse failure instead of failing the listing
Example fix
// before
let parsed: OllamaTagsResponse = serde_json::from_str(payload)
.map_err(|err| anyhow::anyhow!("Failed to parse Ollama /api/tags JSON: {err}"))?;
// after (tolerate non-JSON bodies)
match serde_json::from_str::<OllamaTagsResponse>(payload) {
Ok(parsed) => { /* ... */ }
Err(err) => tracing::warn!("ollama /api/tags not JSON: {err}; body starts: {}", &payload[..payload.len().min(120)]),
} Defensive patterns
Strategy: validation
Validate before calling
function looksLikeOllamaTags(body) {
const j = safeJsonParse(body);
return j && Array.isArray(j.models) && j.models.every(m => typeof m.name === 'string');
} Type guard
fn is_tags_payload(v: &serde_json::Value) -> bool {
v.get("models").map(|m| m.is_array()).unwrap_or(false)
} Try / catch
match parse_ollama_tags_response(&body) {
Ok(tags) => tags,
Err(e) => { log::warn!("ollama catalog unavailable: {e}"); Vec::new() }
} Prevention
- Health-check the Ollama endpoint before parsing
- Log the first bytes of any non-JSON body for diagnosis
- Pin/verify the Ollama version when the schema changes
- Treat local catalog probe failure as non-fatal
When it happens
Trigger: `parse_ollama_tags_response` receiving a body that is empty, truncated, HTML/plain text, or whose `models` entries lack required fields with the expected types.
Common situations: Ollama not running so a proxy/portal answers with HTML; Ollama version with a changed /api/tags schema; a reverse proxy returning an error page; hitting a non-Ollama port.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Cargo metadata is not valid JSON
- Codewhale stream-json line
- Codewhale stream-json line
- deserialize
- <dynamic: wrapped serde_json parse error>
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/3067e88bcf002af4.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/local_ollama.rs:84
#[derive(Debug, Deserialize)]
struct OllamaTagsResponse {
#[serde(default)]
models: Vec<OllamaTagModel>,
}
#[derive(Debug, Deserialize)]
struct OllamaTagModel {
#[serde(default)]
name: String,
#[serde(default)]
model: String,
}
/// Parse native Ollama `GET /api/tags` JSON into sorted unique tag ids.
pub(crate) fn parse_ollama_tags_response(payload: &str) -> anyhow::Result<Vec<String>> {
let parsed: OllamaTagsResponse = serde_json::from_str(payload)
.map_err(|err| anyhow::anyhow!("Failed to parse Ollama /api/tags JSON: {err}"))?;
let mut tags: Vec<String> = parsed
.models
.into_iter()
.filter_map(|row| {
let name = row.name.trim();
if !name.is_empty() {
return Some(name.to_string());
}
let model = row.model.trim();
if !model.is_empty() {
Some(model.to_string())
} else {
None
}
})
.collect();
tags.sort();
tags.dedup();View on GitHub (pinned to 73e0f67d83)