{"record":{"id":"bc7209084d71a4ba","repo":"astrid-runtime/astrid","slug":"signed-channel-version-value-is-not-canonical","errorCode":null,"errorMessage":"signed channel version '{value}' is not canonical semver","messagePattern":"signed channel version '(.+?)' is not canonical semver","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/update_channel.rs","lineNumber":317,"sourceCode":"\nfn is_lower_hex_64(value: &str) -> bool {\n    value.len() == 64\n        && value\n            .bytes()\n            .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))\n}\n\nfn is_commit(value: &str) -> bool {\n    value.len() == 40\n        && value\n            .bytes()\n            .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))\n}\n\nfn canonical_version(value: &str) -> anyhow::Result<semver::Version> {\n    let parsed = semver::Version::parse(value)\n        .with_context(|| format!(\"signed channel version '{value}' is not valid semver\"))?;\n    ensure!(\n        parsed.to_string() == value,\n        \"signed channel version '{value}' is not canonical semver\"\n    );\n    Ok(parsed)\n}\n\nfn canonical_time(value: &str, label: &str) -> anyhow::Result<DateTime<Utc>> {\n    let parsed = DateTime::parse_from_rfc3339(value)\n        .with_context(|| format!(\"signed channel {label} is not RFC3339\"))?\n        .with_timezone(&Utc);\n    ensure!(\n        parsed.to_rfc3339_opts(SecondsFormat::Secs, true) == value,\n        \"signed channel {label} is not canonical UTC RFC3339 seconds\"\n    );\n    Ok(parsed)\n}\n\nfn validate_targets_for(","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/update_channel.rs#L299-L335","documentation":"`canonical_version` parses a version string with `semver::Version::parse` and then requires that re-serializing the parsed version reproduces the input byte-for-byte. This error means the string is valid semver but not in canonical form — e.g. leading zeros in numeric segments (`v01.2.3` style `1.02.3`), uppercase prerelease/build identifiers, or a redundant prefix. Signed channel metadata must use the canonical spelling so signatures bind to a deterministic version string.","triggerScenarios":"`validate_pointer` or `verify_release_extension` receiving a version field like `1.02.3`, `1.2.03`, `1.2.3-alpha.01`, or `V1.2.3` (valid semver, non-canonical serialization).","commonSituations":"Hand-edited channel pointer files; tooling that zero-pads version numbers; a release pipeline emitting uppercase prerelease tags (`1.2.3-RC1` instead of `1.2.3-rc1`); copy-pasting versions with a leading `v` into a field that forbids it.","solutions":["Rewrite the version in canonical form: no leading zeros in numeric identifiers, lowercase prerelease identifiers, no leading `v`.","Regenerate or re-sign the channel metadata/pointer with the canonical version string so the signature covers exactly what is serialized.","Fix the producing pipeline (CI release script) to normalize versions through `semver::Version::parse(v).to_string()` before embedding them.","If only the local pointer file is wrong, update it to the exact tag string used by the release endpoint."],"exampleFix":"// before (channel metadata)\n\"version\": \"1.02.3-RC1\"\n\n// after (canonical semver)\n\"version\": \"1.2.3-rc1\"","handlingStrategy":"validation","validationCode":"// Rust: canonicalize before writing into channel metadata\nlet v = semver::Version::parse(input)?;\nassert_eq!(v.to_string(), input, \"version must be canonical semver\");","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Always normalize versions via `semver::Version::parse(v).to_string()` before signing metadata.","Never zero-pad version segments; keep prerelease identifiers lowercase.","Strip any leading `v`/`V` prefix before storing the version field.","Lint release scripts to reject non-canonical versions at build time."],"tags":["semver","validation","update-channel","rust"],"backgroundTag":"invalid-argument-format","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}