{"record":{"id":"95803ce7d114b0c9","repo":"astrid-runtime/astrid","slug":"read-materialized-capsule-manifest-error","errorCode":null,"errorMessage":"read materialized capsule manifest: {error:#}","messagePattern":"read materialized capsule manifest: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/capsule_materialization.rs","lineNumber":39,"sourceCode":"            .map_err(|error| anyhow::anyhow!(\"resolve capsule cache owner UID: {error}\"))?;\n        let verified = astrid_capsule_install::read_verified_durable_package_for_owner(\n            self.principal_store\n                .as_ref()\n                .ok_or_else(|| anyhow::anyhow!(\"durable capsule registry is unavailable\"))?,\n            &astrid_storage::StateOwner::Principal(uid),\n            manifest.package.name.as_str(),\n        )?\n        .ok_or_else(|| anyhow::anyhow!(\"materialized capsule is absent from durable registry\"))?;\n        if verified.snapshot() != snapshot {\n            anyhow::bail!(\"materialized capsule snapshot differs from the caller's publication\");\n        }\n        if verified.manifest().package.name != manifest.package.name\n            || verified.manifest().package.version != manifest.package.version\n        {\n            anyhow::bail!(\"materialized capsule manifest differs from durable registry\");\n        }\n        let manifest_bytes = Self::read_projection_file_nofollow(&dir.join(\"Capsule.toml\"))\n            .map_err(|error| anyhow::anyhow!(\"read materialized capsule manifest: {error:#}\"))?;\n        if manifest_bytes != verified.manifest_bytes() {\n            anyhow::bail!(\"durable capsule manifest bytes do not match materialization\");\n        }\n        let metadata_bytes = Self::read_projection_file_nofollow(&dir.join(\"meta.json\"))\n            .map_err(|error| anyhow::anyhow!(\"read materialized capsule metadata: {error:#}\"))?;\n        if metadata_bytes != verified.metadata_bytes() {\n            anyhow::bail!(\"durable capsule metadata does not match materialization\");\n        }\n        let authority_bytes = Self::read_projection_file_nofollow(&dir.join(\"authority.json\"))\n            .map_err(|error| anyhow::anyhow!(\"read materialized capsule authority: {error:#}\"))?;\n        if authority_bytes != verified.snapshot().package().authority {\n            anyhow::bail!(\"durable capsule authority bytes do not match materialization\");\n        }\n        let mut expected_files = verified\n            .archive_entries()\n            .map(|(path, bytes)| (path.to_owned(), bytes.to_vec()))\n            .collect::<std::collections::BTreeMap<_, _>>();\n        expected_files.insert(","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/capsule_materialization.rs#L21-L57","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"let meta = fs.symlink_metadata(dir.join(\"Capsule.toml\"))?;\nif !meta.is_file() {\n    return Err(\"Capsule.toml in materialization is not a regular file\");\n}","typeGuard":"fn is_regular_file(path: &Path) -> bool {\n    std::fs::symlink_metadata(path).map(|m| m.is_file()).unwrap_or(false)\n}","tryCatchPattern":"match read_projection_file_nofollow(&dir.join(\"Capsule.toml\")) {\n    Ok(bytes) => verify_manifest_bytes(bytes),\n    Err(e) => {\n        log::error!(\"materialized Capsule.toml unreadable (missing/symlink/perms): {e:#}\");\n        re_materialize(dir)\n    }\n}","preventionTips":["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"],"tags":["file-read","filesystem","capsules"],"backgroundTag":"file-read-failed","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}