astrid-runtime/astrid · critical

durable capsule {id} WASM hash differs between metadata and

Error message

durable capsule {id} WASM hash differs between metadata and archive

What it means

During durable capsule identity verification, the BLAKE3 hash of the WASM component actually present in the archive does not equal the wasm_hash recorded in the capsule's metadata record. The library throws this because metadata is supposed to describe exactly the executable bytes being installed; any divergence means one of the two was regenerated or tampered with. Verification is aborted before the package can be read for an owner.

Source

Thrown at crates/astrid-capsule-install/src/storage.rs:312

    if let Some(component) = manifest.components.first() {
        let Some(relative) = component.path.to_str() else {
            bail!("durable capsule {id} component path is not UTF-8");
        };
        let Some(bytes) = archive_files.get(relative) else {
            bail!("durable capsule {id} component is missing from its archive");
        };
        if Path::new(relative)
            .extension()
            .is_some_and(|extension| extension.eq_ignore_ascii_case("wasm"))
        {
            let archive_hash = blake3::hash(bytes).to_hex().to_string();
            if authority.wasm_hash_pinned
                && authority.approved_wasm_hash.as_deref() != Some(archive_hash.as_str())
            {
                bail!("durable capsule {id} WASM hash differs between authority and archive");
            }
            if metadata.wasm_hash.as_deref() != Some(archive_hash.as_str()) {
                bail!("durable capsule {id} WASM hash differs between metadata and archive");
            }
        } else if metadata.wasm_hash.is_some() {
            bail!("durable capsule {id} metadata names a hash for a non-WASM component");
        }
    } else if metadata.wasm_hash.is_some() {
        bail!("durable capsule {id} metadata names a component absent from its archive");
    }
    let mut effective_capabilities = manifest.capabilities.clone();
    for component in &manifest.components {
        if let Some(capabilities) = &component.capabilities {
            effective_capabilities.merge_from(capabilities);
        }
    }
    if !effective_capabilities
        .expansions_from(&authority.approved_capabilities)
        .is_empty()
    {
        bail!("durable capsule {id} manifest exceeds its authority receipt");

View on GitHub (pinned to affd8760f4)

Solutions

  1. Republish the capsule so metadata and archive are regenerated together (publish_directory_package recomputes hashes atomically).
  2. Recompute blake3 of the archive's WASM component and update metadata.wasm_hash to that value.
  3. If the wrong archive was stored, restore the archive matching the pinned metadata hash from a trusted source.
  4. Check for interrupted publication/concurrent writers that left metadata and archive out of sync.

Example fix

// before: metadata published with stale hash
CapsuleMeta { wasm_hash: Some("old_blake3_hex".into()), .. }
// after: regenerate metadata from the archive being published
let bytes = std::fs::read("component.wasm")?;
meta.wasm_hash = Some(blake3::hash(&bytes).to_hex().to_string());
Defensive patterns

Strategy: validation

Validate before calling

let bytes = archive_files.get(component_path)?;
let archive_hash = blake3::hash(bytes).to_hex().to_string();
if metadata.wasm_hash.as_deref() != Some(archive_hash.as_str()) {
    return Err("metadata wasm_hash does not match archive component");
}

Type guard

fn metadata_matches_archive(meta: &CapsuleMeta, archive_hash: &str) -> bool {
    meta.wasm_hash.as_deref() == Some(archive_hash)
}

Try / catch

match read_verified_durable_package_for_owner(&store, owner, id).await {
    Ok(pkg) => pkg,
    Err(e) if e.to_string().contains("WASM hash differs between metadata and archive") => {
        // republish or restore consistent metadata/archive
    },
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling read_verified_durable_package_for_owner (directly or via the mismatch test) when metadata.wasm_hash differs from blake3(archive component bytes) for a component with a .wasm extension, e.g. after the archive was rebuilt while the metadata row kept an old hash.

Common situations: Publishing a rebuilt WASM binary without refreshing CapsuleMeta; hand-editing metadata rows; partial/corrupted writes where the archive landed but metadata update failed; mirroring or importing a capsule from another environment where the binary was recompiled.

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/ceb1dd8631686514. Report an issue: GitHub.