astrid-runtime/astrid · error
read materialized capsule metadata: {error:#}
Error message
read materialized capsule metadata: {error:#} What it means
During verification of a published capsule materialization, the kernel re-reads the projected meta.json file with read_projection_file_nofollow and wraps any I/O failure with this message. The library throws it because a durable capsule is only valid when every projection file (Capsule.toml, meta.json, authority.json, and archive members) can be read byte-for-byte without following symlinks; if meta.json cannot be read, integrity cannot be proven. It is an I/O-error wrapper, not a content mismatch (mismatches produce separate 'does not match' bail errors).
Source
Thrown at crates/astrid-kernel/src/capsule_materialization.rs:44
&astrid_storage::StateOwner::Principal(uid),
manifest.package.name.as_str(),
)?
.ok_or_else(|| anyhow::anyhow!("materialized capsule is absent from durable registry"))?;
if verified.snapshot() != snapshot {
anyhow::bail!("materialized capsule snapshot differs from the caller's publication");
}
if verified.manifest().package.name != manifest.package.name
|| verified.manifest().package.version != manifest.package.version
{
anyhow::bail!("materialized capsule manifest differs from durable registry");
}
let manifest_bytes = Self::read_projection_file_nofollow(&dir.join("Capsule.toml"))
.map_err(|error| anyhow::anyhow!("read materialized capsule manifest: {error:#}"))?;
if manifest_bytes != verified.manifest_bytes() {
anyhow::bail!("durable capsule manifest bytes do not match materialization");
}
let metadata_bytes = Self::read_projection_file_nofollow(&dir.join("meta.json"))
.map_err(|error| anyhow::anyhow!("read materialized capsule metadata: {error:#}"))?;
if metadata_bytes != verified.metadata_bytes() {
anyhow::bail!("durable capsule metadata does not match materialization");
}
let authority_bytes = Self::read_projection_file_nofollow(&dir.join("authority.json"))
.map_err(|error| anyhow::anyhow!("read materialized capsule authority: {error:#}"))?;
if authority_bytes != verified.snapshot().package().authority {
anyhow::bail!("durable capsule authority bytes do not match materialization");
}
let mut expected_files = verified
.archive_entries()
.map(|(path, bytes)| (path.to_owned(), bytes.to_vec()))
.collect::<std::collections::BTreeMap<_, _>>();
expected_files.insert(
"Capsule.toml".to_owned(),
verified.manifest_bytes().to_vec(),
);
expected_files.insert("meta.json".to_owned(), verified.metadata_bytes().to_vec());
expected_files.insert(View on GitHub (pinned to affd8760f4)
Solutions
- Delete the stale materialization directory and let the kernel re-materialize it (e.g. call repair_published_materialization / ensure_published_materialization, which removes and republishes the projection).
- Check that meta.json exists and is a regular file (not a symlink) inside the materialization dir: ls -la <dir>/meta.json.
- Fix filesystem permissions so the process (or the principal's UID owner) can read the file.
- Re-install or re-publish the capsule so the durable package and projection are regenerated from the snapshot.
Example fix
// before: trusting an existing cache dir that may be incomplete let bound = kernel.load_capsule(&dir, &principal, &manifest)?; // after: route through repair so a broken projection is rebuilt let snapshot = kernel.published_capsule_snapshot(&principal, &manifest)?; let target = kernel.published_cache_target(&principal, &manifest, &snapshot)?; let bound_manifest = kernel.ensure_published_materialization(&target, &principal, &manifest, &snapshot)?;
Defensive patterns
Strategy: validation
Validate before calling
fn meta_projection_readable(dir: &std::path::Path) -> Result<(), String> {
let p = dir.join("meta.json");
let md = std::fs::symlink_metadata(&p)
.map_err(|e| format!("meta.json unreadable: {e}"))?;
if md.file_type().is_symlink() { return Err("meta.json is a symlink".into()); }
if !md.is_file() { return Err("meta.json is not a regular file".into()); }
std::fs::File::open(&p).map_err(|e| format!("meta.json open failed: {e}"))?;
Ok(())
} Type guard
fn has_regular_file(dir: &std::path::Path, name: &str) -> bool {
std::fs::symlink_metadata(dir.join(name))
.map(|md| md.is_file())
.unwrap_or(false)
} Try / catch
match kernel.ensure_published_materialization(&target, &principal, &manifest, &snapshot) {
Ok(m) => /* use m */,
Err(e) if e.to_string().contains("read materialized capsule metadata") => {
let _ = std::fs::remove_dir_all(&target); // drop broken projection
let m = kernel.ensure_published_materialization(&target, &principal, &manifest, &snapshot)?;
}
Err(e) => return Err(e),
} Prevention
- Never manually delete or edit files inside a materialized capsule cache; always re-materialize.
- Before load/confirm, check Capsule.toml, meta.json and authority.json all exist as regular, non-symlink files.
- Keep cache directory permissions aligned with the principal UID that owns the materialization.
- Exclude capsule cache directories from cleanup tools and backup/restore tools that alter file metadata or symlinks.
When it happens
Trigger: verify_published_materialization is called (directly or via repair_published_materialization, confirm_published_materialization, or verify_registry_materialization) while the file <materialization_dir>/meta.json is missing, unreadable, is a symlink (nofollow open refuses), or the process lacks read permission on it. Typical call sites are capsule load/prepare-runtime-replacement flows that repair or confirm the published cache.
Common situations: A partially completed or interrupted materialization left the cache directory without meta.json; an external tool or user deleted files from the capsule cache; a symlink attack or accidental symlink replaced meta.json; permissions were tightened on the cache directory; disk/IO errors while reading a network-mounted cache.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- read materialized capsule authority: {error:#}
- read materialized capsule member {relative}: {error}
- inspect capsule materialization: {error}
- capsule projection contains a symbolic link: {}
- projected path is redirected or not a regular file: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/6f1cb2b771a0817b.
Report an issue: GitHub.