cube-js/cube · error
could not parse release metadata: {e}
Error message
could not parse release metadata: {e} What it means
In `latest_release`, after the GitHub release lookup succeeds, the JSON body is deserialized into the `Release` struct. If the response body isn't valid JSON or doesn't match the `Release` shape, the reqwest JSON error is wrapped as `could not parse release metadata: {e}`. This is a response-shape failure, not a network failure (non-2xx is handled earlier).
Source
Thrown at rust/cube-cli/src/update.rs:79
/// Fetch the latest release metadata from the GitHub API.
pub async fn latest_release(http: &reqwest::Client) -> Result<Release> {
let url = format!(
"{}/repos/{}/releases/latest",
release_api_base(),
release_repo()
);
let res = http
.get(&url)
.header(reqwest::header::ACCEPT, "application/vnd.github+json")
.timeout(Duration::from_secs(10))
.send()
.await?;
if !res.status().is_success() {
bail!("release lookup failed ({}) at {url}", res.status());
}
res.json::<Release>()
.await
.map_err(|e| anyhow!("could not parse release metadata: {e}"))
}
/// Order-compare two dotted versions numerically, segment by segment.
fn newer_than(candidate: &str, current: &str) -> bool {
let parse = |v: &str| -> Vec<u64> {
v.split(['.', '-'])
.map_while(|s| s.parse::<u64>().ok())
.collect()
};
let (a, b) = (parse(candidate), parse(current));
if a.is_empty() || b.is_empty() {
return candidate != current;
}
a > b
}
/// Outcome of the background release check.
#[derive(Debug, Clone, PartialEq, Eq)]View on GitHub (pinned to 7d981676b3)
Solutions
- Check the wrapped reqwest/serde message (`{e}`) for the exact field that failed to deserialize.
- Verify the release metadata URL returns the expected JSON (curl it and compare against the `Release` struct fields).
- Rule out proxies returning HTML with a 200 status.
- Update the CLI — an old `Release` struct may no longer match the current feed shape.
Example fix
// before (body mismatch) could not parse release metadata: missing field `tag_name` // after: verify feed curl -s $RELEASE_URL | jq '.tag_name'
Defensive patterns
Strategy: try-catch
Validate before calling
let body = res.text().await?;
serde_json::from_str::<serde_json::Value>(&body)?; // pre-validate JSON shape
if body.get("tag_name").is_none() { /* abort before calling latest_release */ } Try / catch
match latest_release(url).await {
Err(e) if e.to_string().contains("could not parse release metadata") => {
// inspect raw body / proxy behavior, fall back to current version
}
other => other?,
} Prevention
- Verify the release feed URL returns JSON with a curl before wiring it in
- Check for proxies/captive portals returning 200 + HTML
- Keep the CLI updated so the Release struct matches the feed
When it happens
Trigger: `latest_release` fetches a release metadata URL that returns 2xx but with a body that fails `res.json::<Release>()`: HTML error pages behind proxies, changed/missing fields (e.g. tag name or assets) in the release JSON, or an empty body.
Common situations: Corporate proxy or captive portal returning 200 with HTML, GitHub API shape changes or a misconfigured update-manifest URL in self-hosted setups, running an old CLI against a renamed release feed.
Related errors
- release lookup failed ({}) at {url}
- HTTP error! status: ${response.status}
- unexpected response ${response.statusText}
- ${pkg} dependency not found. Please run this command from pr
- No app secret found
AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02).
Data as JSON: /api/errors/e6f51d9b6f6f1faf.
Report an issue: GitHub.