{"record":{"id":"3d79518e018c2aa6","repo":"BigPizzaV3/CodexPlusPlus","slug":"latest-json-missing-version","errorCode":null,"errorMessage":"latest.json missing version","messagePattern":"latest\\.json missing version","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/update.rs","lineNumber":114,"sourceCode":"            .and_then(Value::as_str)\n            .unwrap_or_default()\n            .to_string(),\n        body: payload\n            .get(\"body\")\n            .and_then(Value::as_str)\n            .unwrap_or_default()\n            .to_string(),\n        asset_name: selected.as_ref().map(|asset| asset.name.clone()),\n        asset_url: selected.map(|asset| asset.browser_download_url),\n    })\n}\n\npub fn release_from_latest_json_payload(payload: &Value) -> anyhow::Result<Release> {\n    let version = payload\n        .get(\"version\")\n        .or_else(|| payload.get(\"tag_name\"))\n        .and_then(Value::as_str)\n        .ok_or_else(|| anyhow::anyhow!(\"latest.json missing version\"))?\n        .to_string();\n    let assets = payload\n        .get(\"assets\")\n        .and_then(Value::as_array)\n        .into_iter()\n        .flatten()\n        .filter_map(|asset| {\n            let name = asset.get(\"name\")?.as_str()?.to_string();\n            let url = asset\n                .get(\"url\")\n                .or_else(|| asset.get(\"browser_download_url\"))?\n                .as_str()?\n                .to_string();\n            Some((name, url))\n        })\n        .collect::<Vec<_>>();\n    let selected = select_update_asset(&assets);\n    Ok(Release {","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/1f431ae49b57b3055e0e6845ba6156c6b4232b4d/crates/codex-plus-core/src/update.rs#L96-L132","documentation":"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.","triggerScenarios":"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\".","commonSituations":"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.","solutions":["Open the manifest URL (DEFAULT_LATEST_JSON_URL or your configured one) with curl and inspect the body","Add a top-level \"version\": \"x.y.z\" string (or \"tag_name\") to latest.json","If the version is emitted as a JSON number, quote it in the generator","Re-run check_for_update after republishing the manifest"],"exampleFix":"// before (latest.json)\n{ \"pub_date\": \"2026-08-16T00:00:00Z\", \"platforms\": { ... } }\n\n// after\n{ \"version\": \"1.4.2\", \"pub_date\": \"2026-08-16T00:00:00Z\", \"platforms\": { ... } }","handlingStrategy":"type-guard","validationCode":"if !latest_json_has_version(&payload) {\n    anyhow::bail!(\"latest.json lacks a usable version: {payload}\");\n}\nlet release = release_from_latest_json_payload(&payload)?;","typeGuard":"fn latest_json_has_version(payload: &serde_json::Value) -> bool {\n    payload\n        .get(\"version\")\n        .or_else(|| payload.get(\"tag_name\"))\n        .and_then(serde_json::Value::as_str)\n        .is_some_and(|v| !v.trim().is_empty())\n}","tryCatchPattern":null,"preventionTips":["Validate the manifest shape in the publishing pipeline before it goes live","Keep manifest generators in the same repo as a schema check in CI","Quote versions as strings in generated JSON"],"tags":["latest-json","update-manifest","json","versioning"],"backgroundTag":"missing-required-json-field","analyzedSha":"1f431ae49b57b3055e0e6845ba6156c6b4232b4d","analyzedAt":"2026-08-16T20:54:18.598Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}