astrid-runtime/astrid · error

materialized capsule member {relative} differs from durable

Error message

materialized capsule member {relative} differs from durable archive

What it means

After confirming the file and directory inventories match, verify_published_materialization reads each expected member byte-for-byte with read_projection_file_nofollow and compares it to the bytes stored in the verified durable package (including Capsule.toml, meta.json, and authority.json). This bail fires when an individual materialized file's contents differ from the durable archive, meaning the on-disk projection has been corrupted, truncated, or tampered with relative to the immutable published package.

Source

Thrown at crates/astrid-kernel/src/capsule_materialization.rs:99

            .map(ToOwned::to_owned)
            .collect::<Vec<_>>();
        expected_directories.extend(archive_directories.iter().cloned());
        expected_directories.extend(
            archive_directories
                .iter()
                .map(String::as_str)
                .flat_map(authenticated_ancestor_directories),
        );
        if actual.directories != expected_directories {
            anyhow::bail!("materialized capsule directory inventory differs from durable package");
        }
        for (relative, expected) in &expected_files {
            let materialized =
                Self::read_projection_file_nofollow(&dir.join(relative)).map_err(|error| {
                    anyhow::anyhow!("read materialized capsule member {relative}: {error}")
                })?;
            if materialized != *expected {
                anyhow::bail!(
                    "materialized capsule member {relative} differs from durable archive"
                );
            }
        }
        let expansions = manifest
            .capabilities
            .expansions_from(&verified.authority().approved_capabilities);
        if !expansions.is_empty() {
            anyhow::bail!("materialized capsule manifest exceeds durable authority approval");
        }
        Ok(())
    }

    /// Bind durable activation or authorize the explicit workspace portal.
    #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
    pub(crate) fn capture_bound_materialization(
        &self,
        dir: &Path,

View on GitHub (pinned to affd8760f4)

Solutions

  1. Trigger repair via repair_published_materialization (or ensure_published_materialization): it removes the divergent tree and re-materializes every member byte-identically from the durable registry snapshot.
  2. Manually delete the capsule's materialized cache directory and reload so it is rebuilt from the durable package.
  3. Identify which member differs from the error context ({relative}) and stop editing files inside the materialized cache — always rebuild instead.
  4. If repairs keep failing, confirm the durable registry entry itself is intact (read_verified_durable_package_for_owner succeeds and its snapshot matches the caller's).

Example fix

// before: patching the divergent file in the cache in place
copy_my_fixed_toml_into(&runtime_dir.join("Capsule.toml"));

// after: rebuild the projection from the immutable snapshot
let manifest = kernel.ensure_published_materialization(
    &runtime_dir, &principal, &discovery_manifest, &snapshot,
)?;
Defensive patterns

Strategy: try-catch

Validate before calling

// Compare the file you suspect against the durable package before loading
let on_disk = std::fs::read(cache_dir.join("Capsule.toml"))?;
if on_disk != verified.manifest_bytes() {
    kernel.repair_published_materialization(&cache_dir, &principal, &manifest, &snapshot)?;
}

Type guard

fn projection_matches(path: &Path, expected: &[u8]) -> bool {
    std::fs::read(path).map(|b| b == expected).unwrap_or(false)
}

Try / catch

if let Err(e) = kernel.load_capsule(&cache_dir, &principal) {
    if e.to_string().contains("differs from durable archive") {
        std::fs::remove_dir_all(&cache_dir)?;
        kernel.ensure_published_materialization(&cache_dir, &principal, &manifest, &snapshot)?;
    } else { return Err(e); }
}

Prevention

When it happens

Trigger: For some (relative, expected) pair from the verified package's archive entries, read_projection_file_nofollow succeeds but the returned bytes do not equal the expected bytes — e.g. an edited Capsule.toml inside the cache, a truncated data file after a disk-full write, or a file overwritten by a different capsule version's projection.

Common situations: A developer edited Capsule.toml in the materialized directory to test a change; a crash or power loss left a partially written file; two different capsule versions were materialized into the same path; a virus scanner or sync tool modified cache contents; an attacker tampered with the projection (this check is the tamper detector).

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