{"record":{"id":"15d64c508490962d","repo":"astrid-runtime/astrid","slug":"inspect-capsule-materialization-error","errorCode":null,"errorMessage":"inspect capsule materialization: {error}","messagePattern":"inspect capsule materialization: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/capsule_materialization.rs","lineNumber":182,"sourceCode":"        snapshot: &astrid_storage::CapsulePackageSnapshot,\n    ) -> anyhow::Result<astrid_capsule_types::manifest::CapsuleManifest> {\n        self.repair_published_materialization(target, principal, manifest, snapshot)\n    }\n\n    /// Replace a canonical stale projection without trusting the old manifest.\n    #[cfg(not(all(target_arch = \"wasm32\", target_os = \"unknown\")))]\n    pub(crate) fn repair_published_materialization(\n        &self,\n        target: &Path,\n        principal: &astrid_core::principal::PrincipalId,\n        discovery_manifest: &astrid_capsule_types::manifest::CapsuleManifest,\n        snapshot: &astrid_storage::CapsulePackageSnapshot,\n    ) -> anyhow::Result<astrid_capsule_types::manifest::CapsuleManifest> {\n        self.validate_published_cache_path(target, principal, discovery_manifest, snapshot)?;\n        let target_metadata = match std::fs::symlink_metadata(target) {\n            Ok(metadata) => Some(metadata),\n            Err(error) if error.kind() == std::io::ErrorKind::NotFound => None,\n            Err(error) => return Err(anyhow::anyhow!(\"inspect capsule materialization: {error}\")),\n        };\n        if let Some(metadata) = target_metadata {\n            if metadata.file_type().is_symlink() || !metadata.is_dir() {\n                anyhow::bail!(\"capsule materialization target is redirected or not a directory\");\n            }\n            if let Ok(bound_manifest) =\n                astrid_capsule::discovery::load_manifest(&target.join(\"Capsule.toml\"))\n                && self\n                    .verify_published_materialization(target, principal, &bound_manifest, snapshot)\n                    .is_ok()\n            {\n                return Ok(bound_manifest);\n            }\n            astrid_core::platform_fs::verify_no_redirects(target).map_err(|error| {\n                anyhow::anyhow!(\"capsule materialization target is redirected: {error}\")\n            })?;\n            std::fs::remove_dir_all(target).map_err(|error| {\n                anyhow::anyhow!(\"remove stale capsule materialization: {error}\")","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/capsule_materialization.rs#L164-L200","documentation":"At the start of repair_published_materialization, the kernel calls std::fs::symlink_metadata on the materialization target to decide whether the existing projection is intact, stale, or absent. NotFound is treated as 'nothing there yet', but any other stat failure (e.g. permission denied on an ancestor, IO error, too many symlinks) aborts with this message rather than risk deleting or overwriting a target whose state is unknown. The library throws it because blindly repairing a directory it cannot even stat would be unsafe.","triggerScenarios":"repair_published_materialization (called by capture_bound_materialization or ensure_published_materialization) runs while symlink_metadata(target) fails with an error other than NotFound — e.g. a parent directory is not searchable (permission denied), the path is on an unavailable/failed mount, the path exceeds filesystem limits, or an IO error occurs.","commonSituations":"Cache directory owned by another user/UID after running under a different account; NFS/network mount offline or stale; SELinux/AppArmor denying access to the cache path; overly restrictive permissions on an ancestor directory; a very long path or corrupted directory entry.","solutions":["Fix access to the target path and its ancestors (chown/chmod so the process or the principal's UID can traverse and stat it).","Check that the parent mount/filesystem is available (df/mount; remount a stale NFS cache).","Verify no MAC policy (SELinux/AppArmor) is blocking access (check audit logs, adjust policy or context).","If the target is genuinely broken and safe to remove, remove it manually so symlink_metadata returns NotFound and the repair path can proceed."],"exampleFix":"// before: repair fails because the cache dir is owned by root\nlet m = kernel.ensure_published_materialization(&target, &principal, &manifest, &snapshot)?;\n\n// after: restore ownership, then repair\n// $ sudo chown -R <principal-uid>:<principal-gid> /path/to/cache/target\nlet m = kernel.ensure_published_materialization(&target, &principal, &manifest, &snapshot)?;","handlingStrategy":"try-catch","validationCode":"fn target_stattable(target: &std::path::Path) -> Result<bool, String> {\n    match std::fs::symlink_metadata(target) {\n        Ok(_) => Ok(true),\n        Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(false),\n        Err(e) => Err(format!(\"target not inspectable: {e}\")),\n    }\n}","typeGuard":"fn is_inspectable_dir(target: &std::path::Path) -> bool {\n    std::fs::symlink_metadata(target)\n        .map(|md| !md.file_type().is_symlink() && md.is_dir())\n        .unwrap_or(false)\n}","tryCatchPattern":"match kernel.ensure_published_materialization(&target, &principal, &manifest, &snapshot) {\n    Ok(m) => /* proceed */,\n    Err(e) if e.to_string().contains(\"inspect capsule materialization\") => {\n        // stat failed: check mount/permissions, or remove broken target so\n        // repair can take the NotFound path\n        let _ = std::fs::remove_dir_all(&target);\n        let m = kernel.ensure_published_materialization(&target, &principal, &manifest, &snapshot)?;\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Run all capsule operations under the same user/UID that owns the cache tree.","Do not place capsule caches on flaky network mounts; prefer local disk.","Verify ancestor directories grant traverse (x) permission to the process user before repair.","Check for SELinux/AppArmor denials if stat fails with permission-denied on expected-readable paths."],"tags":["io","filesystem","stat","capsule","materialization"],"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"}