astrid-runtime/astrid · error
signed channel version '{value}' is not canonical semver
Error message
signed channel version '{value}' is not canonical semver What it means
`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.
Source
Thrown at crates/astrid-cli/src/commands/update_channel.rs:317
fn is_lower_hex_64(value: &str) -> bool {
value.len() == 64
&& value
.bytes()
.all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
}
fn is_commit(value: &str) -> bool {
value.len() == 40
&& value
.bytes()
.all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
}
fn canonical_version(value: &str) -> anyhow::Result<semver::Version> {
let parsed = semver::Version::parse(value)
.with_context(|| format!("signed channel version '{value}' is not valid semver"))?;
ensure!(
parsed.to_string() == value,
"signed channel version '{value}' is not canonical semver"
);
Ok(parsed)
}
fn canonical_time(value: &str, label: &str) -> anyhow::Result<DateTime<Utc>> {
let parsed = DateTime::parse_from_rfc3339(value)
.with_context(|| format!("signed channel {label} is not RFC3339"))?
.with_timezone(&Utc);
ensure!(
parsed.to_rfc3339_opts(SecondsFormat::Secs, true) == value,
"signed channel {label} is not canonical UTC RFC3339 seconds"
);
Ok(parsed)
}
fn validate_targets_for(View on GitHub (pinned to affd8760f4)
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.
Example fix
// before (channel metadata) "version": "1.02.3-RC1" // after (canonical semver) "version": "1.2.3-rc1"
Defensive patterns
Strategy: validation
Validate before calling
// Rust: canonicalize before writing into channel metadata let v = semver::Version::parse(input)?; assert_eq!(v.to_string(), input, "version must be canonical semver");
Type guard
null
Try / catch
null
Prevention
- 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.
When it happens
Trigger: `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).
Common situations: 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.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- nightly channel must point to an exact nightly prerelease
- stable and dev channels must point to canonical releases
- opaque capsule asset directories cannot be symlinks: {}
- durable capsule {} has malformed contracts pin
- refusing to seed canonical from a non-content-address contra
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/bc7209084d71a4ba.
Report an issue: GitHub.