astrid-runtime/astrid · error

materialized capsule digest does not match durable registry

Error message

materialized capsule digest does not match durable registry

What it means

This error means the digest recorded in the cache path (components[2]) does not match the blake3 hash of the package archive bytes in the loaded snapshot. The kernel throws it as an integrity check: the cached bytes on disk differ from what the cache key claims, so the materialized capsule may be corrupted, tampered with, or stale relative to the durable registry content.

Solutions

  1. Delete the mismatched cache entry (<cache-root>/<uid>/<name>/<wrong-digest>) and re-materialize the capsule from the registry
  2. Re-download/re-fetch the capsule to get a pristine archive, then retry
  3. Check disk health and exclude the cache directory from backup/AV mutation if files were modified externally
  4. If the cache is seeded by tooling, compute the blake3 digest of the final archive bytes and use that as the directory name

Example fix

// before: labeling cache with a digest from metadata
let dir = cache_root.join(name).join(manifest_digest.clone());

// after: digest must hash the actual archive bytes
let digest = blake3::hash(&archive_bytes).to_hex().to_string();
let dir = cache_root.join(name).join(digest);
Defensive patterns

Strategy: validation

Validate before calling

let digest = blake3::hash(&snapshot.package().archive).to_hex().to_string();
if digest != expected_digest { return Err("archive digest mismatch before materialization"); }

Try / catch

match result {
    Err(e) if e.to_string().contains("digest does not match") => {
        // purge the bad cache entry and re-fetch from the registry
    }
    other => other?,
}

Prevention

When it happens

Trigger: During cache validation after materialization: blake3::hash(snapshot.package().archive) differs from the digest component of the cache directory name — e.g. truncated or partially-written archive, an archive overwritten after the cache dir was created, bit-rot on disk, or a cache entry seeded with the wrong digest label.

Common situations: Interrupted download/materialization leaving a partial archive; antivirus or backup tooling modifying files in the cache; manually editing or replacing cache contents; disk corruption; reusing a cache volume copied inconsistently (digest dir name from one version, bytes from another).

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

Appendix: source

Thrown at crates/astrid-kernel/src/lib.rs:1610

                    "capsule cache path contains unsafe components"
                )),
            })
            .collect::<anyhow::Result<_>>()?;
        if components.len() != 3 {
            anyhow::bail!("capsule cache path does not contain owner/id/digest components");
        }
        let uid = self
            .principal_directory
            .uid_for(principal)
            .map_err(|error| anyhow::anyhow!("resolve capsule cache owner UID: {error}"))?;
        if components[0] != uid.to_string() || components[1] != manifest.package.name {
            anyhow::bail!("capsule cache owner or id does not match authenticated registry scope");
        }
        let digest = blake3::hash(&snapshot.package().archive)
            .to_hex()
            .to_string();
        if components[2] != digest {
            anyhow::bail!("materialized capsule digest does not match durable registry");
        }
        Ok(())
    }

    /// Inventory a projection without traversing redirects or special files.
    #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
    fn inventory_projection_files(root: &Path) -> anyhow::Result<ProjectionInventory> {
        fn walk(
            root: &Path,
            directory: &Path,
            inventory: &mut ProjectionInventory,
        ) -> anyhow::Result<()> {
            for entry in std::fs::read_dir(directory).map_err(|error| {
                anyhow::anyhow!("read capsule projection {}: {error}", directory.display())
            })? {
                let entry = entry
                    .map_err(|error| anyhow::anyhow!("read capsule projection entry: {error}"))?;
                let path = entry.path();

View on GitHub (pinned to affd8760f4)