astrid-runtime/astrid · critical · anyhow::Error

immutable release manifest does not match the channel BLAKE3

Error message

immutable release manifest does not match the channel BLAKE3 digest

What it means

The BLAKE3 hash of the downloaded release manifest bytes does not equal the metadata_blake3 digest recorded in the signed channel pointer. This is an integrity check: the manifest has been corrupted, replaced, or the pointer is stale.

Source

Thrown at crates/astrid-cli/src/commands/update_channel.rs:570

        || kind != "nightly"
        || date.len() != 8
        || !date.bytes().all(|byte| byte.is_ascii_digit())
        || NaiveDate::parse_from_str(date, "%Y%m%d").is_err()
        || commit.len() != 40
        || !commit
            .bytes()
            .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
    {
        return None;
    }
    Some(commit)
}

pub(super) fn verify_release_manifest(
    bytes: &[u8],
    pointer: &ChannelPointer,
) -> anyhow::Result<()> {
    ensure!(
        blake3::hash(bytes).to_hex().as_str() == pointer.release.metadata_blake3,
        "immutable release manifest does not match the channel BLAKE3 digest"
    );
    let text = std::str::from_utf8(bytes).context("release manifest is not UTF-8")?;
    let manifest: ReleaseManifest =
        toml::from_str(text).context("release manifest is invalid TOML")?;
    ensure!(
        manifest.schema_version == 1
            && manifest.kind == "astrid-release"
            && manifest.product == PRODUCT
            && manifest.repository == REPOSITORY,
        "release manifest identity is invalid"
    );
    ensure!(
        manifest.version == pointer.release.version
            && manifest.tag == pointer.release.tag
            && manifest.source_commit == pointer.release.source_commit
            && manifest.release_workflow_identity == pointer.release.release_workflow_identity,

View on GitHub (pinned to affd8760f4)

Solutions

  1. Re-fetch the channel pointer and manifest to rule out transient corruption
  2. Re-publish the channel so metadata_blake3 matches the current manifest
  3. Check for caching proxies/CDN serving outdated manifests
  4. Clear the local channel state and retry resolution
Defensive patterns

Strategy: retry

Validate before calling

let expected = &pointer.release.metadata_blake3;
if blake3::hash(bytes).to_hex().as_str() != expected {
    eprintln!("manifest digest mismatch; re-fetch");
}

Try / catch

loop {
    match fetch_and_verify_manifest(&pointer) {
        Ok(m) => break m,
        Err(e) if attempts < 3 => attempts += 1,
        Err(e) => return Err(e),
    }
}

Prevention

When it happens

Trigger: verify_release_manifest (called from resolve_signed_channel) hashes the fetched manifest bytes and compares against pointer.release.metadata_blake3; mismatch occurs on truncated downloads, a re-published manifest without updating the pointer, or a tampered manifest.

Common situations: Proxy/CDN serving a stale or modified manifest; publisher forgot to re-sign the pointer after editing the manifest; disk corruption in the state directory.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/936099baf2adf332. Report an issue: GitHub.