{"record":{"id":"9f3d5ca566d376a0","repo":"BigPizzaV3/CodexPlusPlus","slug":"release-payload-missing-tag-name","errorCode":null,"errorMessage":"release payload missing tag_name","messagePattern":"release payload missing tag_name","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/update.rs","lineNumber":77,"sourceCode":"        .split('.')\n        .map(|part| part.parse::<u64>().map_err(Into::into))\n        .collect()\n}\n\npub fn is_newer_version(candidate: &str, current: &str) -> anyhow::Result<bool> {\n    let mut left = parse_version_tag(candidate)?;\n    let mut right = parse_version_tag(current)?;\n    let len = left.len().max(right.len());\n    left.resize(len, 0);\n    right.resize(len, 0);\n    Ok(left > right)\n}\n\npub fn release_from_github_payload(payload: &Value) -> anyhow::Result<Release> {\n    let version = payload\n        .get(\"tag_name\")\n        .and_then(Value::as_str)\n        .ok_or_else(|| anyhow::anyhow!(\"release payload missing tag_name\"))?\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            Some((\n                asset.get(\"name\")?.as_str()?.to_string(),\n                asset.get(\"browser_download_url\")?.as_str()?.to_string(),\n            ))\n        })\n        .collect::<Vec<_>>();\n    let selected = select_update_asset(&assets);\n    Ok(Release {\n        version,\n        url: payload\n            .get(\"html_url\")","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/1f431ae49b57b3055e0e6845ba6156c6b4232b4d/crates/codex-plus-core/src/update.rs#L59-L95","documentation":"release_from_github_payload maps a GitHub releases API object into a Release. The first required field is tag_name, read via payload.get(\"tag_name\").and_then(Value::as_str); when it is absent (or not a string) the function bails with \"release payload missing tag_name\" before examining anything else. So any parseable JSON body without a top-level string tag_name triggers it.","triggerScenarios":"Feeding release_from_github_payload JSON that is not a release object: a GitHub error body like {\"message\":\"Not Found\", ...} from a wrong owner/repo or wrong endpoint, a rate-limit response, or a test fixture that copied only the assets array.","commonSituations":"Wrong repo owner/name in the API URL; querying an endpoint that returns an error object instead of a release; GitHub unauthenticated rate limiting (60 req/h); test fixtures written from memory instead of recorded responses.","solutions":["Log/inspect the actual payload passed in and confirm it is a release object","Fix the URL to a real releases endpoint (e.g. https://api.github.com/repos/<owner>/<repo>/releases/latest) and check owner/repo spelling","Handle GitHub rate limits: authenticate the request or back off","In tests, add \"tag_name\": \"v0.1.0\" to the fixture"],"exampleFix":"// before\nlet release = release_from_github_payload(&payload)?;\n\n// after\nlet release = release_from_github_payload(&payload).map_err(|err| {\n    let keys = payload.as_object().map(|m| m.keys().collect::<Vec<_>>());\n    anyhow::anyhow!(\"{err} (payload keys: {keys:?})\")\n})?;","handlingStrategy":"type-guard","validationCode":"if !is_github_release_payload(&payload) {\n    anyhow::bail!(\"not a GitHub release payload: {payload}\");\n}\nlet release = release_from_github_payload(&payload)?;","typeGuard":"fn is_github_release_payload(payload: &serde_json::Value) -> bool {\n    payload\n        .get(\"tag_name\")\n        .and_then(serde_json::Value::as_str)\n        .is_some_and(|tag| !tag.trim().is_empty())\n}","tryCatchPattern":null,"preventionTips":["Always hit documented release endpoints and log the response body on non-200","Authenticate GitHub API calls to avoid rate-limit error bodies reaching the parser","Record real API responses for test fixtures instead of writing them by hand"],"tags":["github-api","release","json","update-check"],"backgroundTag":"missing-required-json-field","analyzedSha":"1f431ae49b57b3055e0e6845ba6156c6b4232b4d","analyzedAt":"2026-08-16T20:54:18.598Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}