astrid-runtime/astrid · error
read materialized capsule manifest: {error:#}
Error message
read materialized capsule manifest: {error:#} What it means
Verification reads the materialized Capsule.toml from the capsule directory without following symlinks; this I/O or policy failure is wrapped with the "read materialized capsule manifest:" prefix. Even if the durable registry record exists, the on-disk manifest must be readable to compare bytes.
Source
Thrown at crates/astrid-kernel/src/capsule_materialization.rs:39
.map_err(|error| anyhow::anyhow!("resolve capsule cache owner UID: {error}"))?;
let verified = astrid_capsule_install::read_verified_durable_package_for_owner(
self.principal_store
.as_ref()
.ok_or_else(|| anyhow::anyhow!("durable capsule registry is unavailable"))?,
&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(View on GitHub (pinned to affd8760f4)
Solutions
- Read the wrapped error to distinguish missing file vs symlink rejection vs permissions
- Ensure Capsule.toml in the projection directory is a regular file owned by the resolved UID
- Re-materialize the capsule (repair/confirm flow) to restore a valid manifest file
- Fix directory permissions or the storage volume if the read failed at the OS level
Defensive patterns
Strategy: try-catch
Validate before calling
let meta = fs.symlink_metadata(dir.join("Capsule.toml"))?;
if !meta.is_file() {
return Err("Capsule.toml in materialization is not a regular file");
} Type guard
fn is_regular_file(path: &Path) -> bool {
std::fs::symlink_metadata(path).map(|m| m.is_file()).unwrap_or(false)
} Try / catch
match read_projection_file_nofollow(&dir.join("Capsule.toml")) {
Ok(bytes) => verify_manifest_bytes(bytes),
Err(e) => {
log::error!("materialized Capsule.toml unreadable (missing/symlink/perms): {e:#}");
re_materialize(dir)
}
} Prevention
- Ensure capsule tooling writes Capsule.toml as a regular file, never a symlink
- Keep cache directory ownership aligned with the resolved principal UID
- Monitor the capsule cache volume for disk errors and partial deletions
When it happens
Trigger: Self::read_projection_file_nofollow(&dir.join("Capsule.toml")) inside verify_published_materialization returns Err (file missing, permission problem, or the no-follow policy rejecting a symlinked Capsule.toml), and the error is mapped into this message.
Common situations: Capsule.toml was replaced by a symlink (tampering or bad tooling) and nofollow read rejects it; the cache directory was partially deleted or has wrong ownership/permissions after UID changes; disk errors on the cache volume.
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
- capsules are installed, but granting capsule access failed:
- preserved
- leftover capsule authority receipt is not a regular file: {}
- quarantined capsule authority bytes changed: {}
- leftover capsule authority receipt changed before retirement
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/95803ce7d114b0c9.
Report an issue: GitHub.