{"record":{"id":"4d0a289b586fdac4","repo":"BigPizzaV3/CodexPlusPlus","slug":"invalid-version-tag-value","errorCode":null,"errorMessage":"Invalid version tag: {value}","messagePattern":"Invalid version tag: (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/update.rs","lineNumber":56,"sourceCode":"#[derive(Debug, Clone, PartialEq, Eq, Serialize)]\npub struct UpdateInstall {\n    pub release: Release,\n    pub installer_path: PathBuf,\n    pub launched: bool,\n}\n\npub fn parse_version_tag(value: &str) -> anyhow::Result<Vec<u64>> {\n    let normalized = value.trim().trim_start_matches(['v', 'V']);\n    let mut digits = String::new();\n    for ch in normalized.chars() {\n        if ch.is_ascii_digit() || ch == '.' {\n            digits.push(ch);\n        } else {\n            break;\n        }\n    }\n    if digits.is_empty() {\n        anyhow::bail!(\"Invalid version tag: {value}\");\n    }\n    digits\n        .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","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/1f431ae49b57b3055e0e6845ba6156c6b4232b4d/crates/codex-plus-core/src/update.rs#L38-L74","documentation":"parse_version_tag strips an optional leading v/V, then consumes the longest prefix made only of ASCII digits and dots; if nothing remains it bails with \"Invalid version tag: {value}\". It is called by is_newer_version(candidate, current) — and therefore by check_for_update — for both the remote release version and the locally compiled-in version. Any string that does not start with a digit after the optional v prefix fails: \"dev\", \"latest\", \"nightly\", \"beta2\", \"\".","triggerScenarios":"is_newer_version(\"dev\", \"0.3.1\"), check_for_update(\"0.0.0-dev\"), or parse_version_tag(\"v\") — no digits survive normalization. Tags like \"v1.2.3-rc.1\" are accepted (parsed as 1.2.3; iteration stops at the first non-digit).","commonSituations":"Local/debug builds stamped with channel-style versions; a latest.json whose version field holds a name instead of a number; a release tagged \"main\"; CI passing a branch name instead of the tag into the update check.","solutions":["Ensure both strings handed to is_newer_version look like vX.Y.Z with a leading digit after the optional v prefix","Pass the real release tag from latest.json / GitHub instead of a branch or build-channel name","If arbitrary strings can reach the call, pre-normalize: extract the first digit-and-dot run or default to \"0\"","In UI flows, treat a parse failure as 'no update available' instead of propagating the error"],"exampleFix":"// before\nlet newer = is_newer_version(&release.version, current_version)?;\n\n// after\nfn safe_is_newer(candidate: &str, current: &str) -> bool {\n    is_newer_version(candidate, current).unwrap_or(false)\n}\nlet newer = safe_is_newer(&release.version, current_version);","handlingStrategy":"validation","validationCode":"fn is_parseable_version_tag(value: &str) -> bool {\n    value\n        .trim()\n        .trim_start_matches(['v', 'V'])\n        .chars()\n        .next()\n        .is_some_and(|c| c.is_ascii_digit())\n}\n\nassert!(is_parseable_version_tag(\"v1.2.3\"));\nassert!(!is_parseable_version_tag(\"dev\"));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Stamp builds only with numeric semver versions (CI: git describe --tags --match 'v*')","Validate manifest versions with the predicate above before driving update logic","Treat version parse errors as 'no update' rather than hard failures in the UI"],"tags":["versioning","semver","update-check","parse"],"backgroundTag":"invalid-version-string","analyzedSha":"1f431ae49b57b3055e0e6845ba6156c6b4232b4d","analyzedAt":"2026-08-16T20:54:18.598Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}