BigPizzaV3/CodexPlusPlus · error · anyhow::Error

latest.json missing version

Error message

latest.json missing version

What it means

release_from_latest_json_payload parses the app's own latest.json update manifest. The version is read from payload.get("version").or_else(payload.get("tag_name")) and must be a JSON string; if neither key exists or neither is a string, it bails with "latest.json missing version". This runs inside fetch_latest_release / check_for_update, so a malformed manifest fails the whole update check.

Source

Thrown at crates/codex-plus-core/src/update.rs:114

            .and_then(Value::as_str)
            .unwrap_or_default()
            .to_string(),
        body: payload
            .get("body")
            .and_then(Value::as_str)
            .unwrap_or_default()
            .to_string(),
        asset_name: selected.as_ref().map(|asset| asset.name.clone()),
        asset_url: selected.map(|asset| asset.browser_download_url),
    })
}

pub fn release_from_latest_json_payload(payload: &Value) -> anyhow::Result<Release> {
    let version = payload
        .get("version")
        .or_else(|| payload.get("tag_name"))
        .and_then(Value::as_str)
        .ok_or_else(|| anyhow::anyhow!("latest.json missing version"))?
        .to_string();
    let assets = payload
        .get("assets")
        .and_then(Value::as_array)
        .into_iter()
        .flatten()
        .filter_map(|asset| {
            let name = asset.get("name")?.as_str()?.to_string();
            let url = asset
                .get("url")
                .or_else(|| asset.get("browser_download_url"))?
                .as_str()?
                .to_string();
            Some((name, url))
        })
        .collect::<Vec<_>>();
    let selected = select_update_asset(&assets);
    Ok(Release {

View on GitHub (pinned to 1f431ae49b)

Solutions

  1. Open the manifest URL (DEFAULT_LATEST_JSON_URL or your configured one) with curl and inspect the body
  2. Add a top-level "version": "x.y.z" string (or "tag_name") to latest.json
  3. If the version is emitted as a JSON number, quote it in the generator
  4. Re-run check_for_update after republishing the manifest

Example fix

// before (latest.json)
{ "pub_date": "2026-08-16T00:00:00Z", "platforms": { ... } }

// after
{ "version": "1.4.2", "pub_date": "2026-08-16T00:00:00Z", "platforms": { ... } }
Defensive patterns

Strategy: type-guard

Validate before calling

if !latest_json_has_version(&payload) {
    anyhow::bail!("latest.json lacks a usable version: {payload}");
}
let release = release_from_latest_json_payload(&payload)?;

Type guard

fn latest_json_has_version(payload: &serde_json::Value) -> bool {
    payload
        .get("version")
        .or_else(|| payload.get("tag_name"))
        .and_then(serde_json::Value::as_str)
        .is_some_and(|v| !v.trim().is_empty())
}

Prevention

When it happens

Trigger: A latest.json that only has {"pub_date": ..., "platforms": {...}} (Tauri-style manifest) with no top-level version; a manifest where the version is a JSON number ({"version": 12}) instead of a string; a typo'd key like "Version" or "ver".

Common situations: Migrating between updater manifest conventions; a CI manifest generator whose version variable is empty so the key is omitted; hand-edited manifests; publishing the version as a number.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@1f431ae49b (2026-08-16). Data as JSON: /api/errors/3d79518e018c2aa6. Report an issue: GitHub.