astrid-runtime/astrid · error
materialized capsule directory inventory differs from durabl
Error message
materialized capsule directory inventory differs from durable package
What it means
During published-materialization verification, the kernel inventories every directory present on disk under the materialized capsule cache and compares it to the set of directories implied by the durable registry package (archive directories plus ancestor directories of each archived file). This bail fires when the on-disk directory set differs from that expected set, i.e. the projected cache has extra, missing, or renamed directories relative to the immutable package snapshot. It exists to catch tampering, partial extraction, or stale generations of the cache rather than trusting the filesystem layout.
Source
Thrown at crates/astrid-kernel/src/capsule_materialization.rs:91
anyhow::bail!("materialized capsule file inventory differs from durable package");
}
let mut expected_directories = expected_files
.keys()
.flat_map(|relative| authenticated_ancestor_directories(relative))
.collect::<std::collections::BTreeSet<_>>();
let archive_directories = verified
.archive_directories()
.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");
}View on GitHub (pinned to affd8760f4)
Solutions
- Let the library repair the projection instead of fixing files by hand: call repair_published_materialization (or the load/ensure path that routes through it), which detects the failed verification, removes the stale tree with remove_dir_all, and re-materializes from the durable snapshot.
- Delete the capsule's materialized cache directory for that principal/package and re-run the load so it is re-materialized from the registry snapshot.
- Check that nothing external (editors, sync clients, cleanup scripts) mutates the cache directory between runs, then re-materialize.
- If the difference is intentional, verify the durable package snapshot is the correct publication; a mismatched snapshot id (checked earlier as 'snapshot differs from caller's publication') means the caller passed the wrong CapsulePackageSnapshot.
Example fix
// before: hand-editing or reusing a stale cache directory
let manifest = kernel.load_capsule(&cached_dir, &principal)?;
// after: force repair/re-materialization from the durable snapshot
let manifest = kernel.repair_published_materialization(
&runtime_dir,
&principal,
&discovery_manifest,
&snapshot,
)?; Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-check directory inventory before calling the API
let expected_dirs: BTreeSet<String> = snapshot_archive_directories()
.into_iter()
.chain(expected_files.iter().flat_map(|p| ancestor_dirs(p)))
.collect();
let actual_dirs = list_directories(&cache_dir)?;
if actual_dirs != expected_dirs {
// schedule a repair instead of load
kernel.repair_published_materialization(&cache_dir, &principal, &manifest, &snapshot)?;
} Type guard
fn is_plain_directory(path: &Path) -> bool {
std::fs::symlink_metadata(path)
.map(|m| m.is_dir())
.unwrap_or(false)
} Try / catch
match kernel.load_capsule(&cache_dir, &principal) {
Ok(m) => m,
Err(e) if e.to_string().contains("directory inventory differs") => {
kernel.repair_published_materialization(&cache_dir, &principal, &manifest, &snapshot)?
}
Err(e) => return Err(e),
} Prevention
- Never create, rename, or delete directories inside the materialized capsule cache manually.
- Exclude cache directories from backup/sync tools (Dropbox, Nextcloud, dotfile managers).
- Route every cache-mismatch through repair_published_materialization instead of hand-patching.
- Keep cache directories on local disk, not on network filesystems prone to partial updates.
When it happens
Trigger: verify_published_materialization is called by repair_published_materialization, confirm_published_materialization, or verify_registry_materialization, and Self::inventory_projection_files(dir) returns a `directories` BTreeSet that is not exactly equal to the set built from verified archive directories plus authenticated_ancestor_directories of every expected file path — e.g. an empty directory left behind by a prior generation, a deleted subdirectory, or a symlinked/renamed folder.
Common situations: A previous capsule version's cache was only partially removed before a new generation was projected; a user or tooling manually added or deleted directories inside the cache path; a backup/sync tool (or interrupted process) left extra empty directories; the package snapshot in the durable registry was republished with a different directory layout while the old projection remained on disk.
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
- installed WASM executable differs from its authority receipt
- capsule content changed after authority decision (approved {
- capsule '{}' changed after authority review (approved {}, fo
- leftover capsule authority receipt is not a regular file: {}
- legacy capsule authority root is not a regular directory: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/84f478ed14adde5c.
Report an issue: GitHub.