{"record":{"id":"fc148a03add89ed7","repo":"astrid-runtime/astrid","slug":"nightly-channel-must-point-to-an-exact-nightly-pre","errorCode":null,"errorMessage":"nightly channel must point to an exact nightly prerelease","messagePattern":"nightly channel must point to an exact nightly prerelease","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/update_channel.rs","lineNumber":504,"sourceCode":"            .context(\"channel publication skew overflowed the clock\")?;\n        ensure!(\n            published <= latest_reasonable_publication,\n            \"signed channel published-at is unreasonably far in the future\"\n        );\n    }\n    let max_lifetime = match expected_channel {\n        UpdateChannel::Stable => chrono::Duration::days(30),\n        UpdateChannel::Dev => chrono::Duration::days(7),\n        UpdateChannel::Nightly => chrono::Duration::days(2),\n    };\n    ensure!(\n        expires.signed_duration_since(published) <= max_lifetime,\n        \"signed channel lifetime exceeds the maximum for its channel\"\n    );\n    let version = canonical_version(&pointer.release.version)?;\n    let nightly_commit = nightly_source_commit(&version);\n    match expected_channel {\n        UpdateChannel::Nightly => ensure!(\n            nightly_commit.is_some() && version.build.is_empty(),\n            \"nightly channel must point to an exact nightly prerelease\"\n        ),\n        UpdateChannel::Stable | UpdateChannel::Dev => ensure!(\n            version.pre.is_empty() && version.build.is_empty(),\n            \"stable and dev channels must point to canonical releases\"\n        ),\n    }\n    ensure!(\n        pointer.release.tag == format!(\"v{version}\"),\n        \"signed channel release tag does not match its version\"\n    );\n    ensure!(\n        is_commit(&pointer.release.source_commit),\n        \"signed channel source commit is invalid\"\n    );\n    if let Some(commit) = nightly_commit {\n        ensure!(","sourceCodeStart":486,"sourceCodeEnd":522,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/update_channel.rs#L486-L522","documentation":"A nightly channel pointer must reference an exact nightly prerelease version: the version's pre-release segment must encode the nightly source commit (via nightly_source_commit) and the build metadata must be empty. This ensures a nightly pointer pins one reproducible build rather than a generic or mislabeled version.","triggerScenarios":"parse_channel or enforce_continuity with expected_channel = Nightly and a release version like `0.5.0` (no pre-release) or `0.5.0-nightly.20260909+abc` (build metadata present), i.e. missing the embedded commit or carrying non-empty `version.build`.","commonSituations":"Publisher pointing the nightly channel at a stable/dev release by mistake; someone stripping build/pre segments when normalizing the version; a template pointer reused across channels without rewriting the version.","solutions":["Set the nightly version to the canonical prerelease form `X.Y.Z-nightly.<date>.<commit>` with no build metadata and republish","If you meant to publish a normal release, use the stable or dev channel instead","Re-run the nightly release workflow so it embeds the source commit in the pre-release segment","Do not 'clean' the version string before writing it into the nightly pointer"],"exampleFix":"// before (channel.toml)\nversion = \"0.5.0\"\n// after\nversion = \"0.5.0-nightly.20260909.a1b2c3d\"","handlingStrategy":"validation","validationCode":"fn nightly_version_ok(v: &semver::Version) -> bool {\n    !v.pre.is_empty() && v.pre.as_str().starts_with(\"nightly\") && v.build.is_empty()\n}","typeGuard":"fn is_nightly_prerelease(version: &str) -> bool {\n    match semver::Version::parse(version) {\n        Ok(v) => !v.build.is_empty() == false && !v.pre.is_empty(),\n        Err(_) => false,\n    }\n}","tryCatchPattern":"match parse_channel(&bytes, UpdateChannel::Nightly, now) {\n    Err(e) if e.to_string().contains(\"exact nightly prerelease\") => {\n        anyhow::bail!(\"nightly channel points at a non-nightly version: {}\", /* logged version */)\n    }\n    other => other?,\n}","preventionTips":["Generate nightly versions only via the nightly release workflow","Never strip pre-release/build segments when copying versions between files","Keep stable/dev pointers and nightly pointers in separate templates","Validate the version shape before signing nightly metadata"],"tags":["update-channel","semver","validation"],"backgroundTag":"invalid-argument-value","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"}