astrid-runtime/astrid · error
inspect capsule materialization: {error}
Error message
inspect capsule materialization: {error} What it means
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.
Source
Thrown at crates/astrid-kernel/src/capsule_materialization.rs:182
snapshot: &astrid_storage::CapsulePackageSnapshot,
) -> anyhow::Result<astrid_capsule_types::manifest::CapsuleManifest> {
self.repair_published_materialization(target, principal, manifest, snapshot)
}
/// Replace a canonical stale projection without trusting the old manifest.
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub(crate) fn repair_published_materialization(
&self,
target: &Path,
principal: &astrid_core::principal::PrincipalId,
discovery_manifest: &astrid_capsule_types::manifest::CapsuleManifest,
snapshot: &astrid_storage::CapsulePackageSnapshot,
) -> anyhow::Result<astrid_capsule_types::manifest::CapsuleManifest> {
self.validate_published_cache_path(target, principal, discovery_manifest, snapshot)?;
let target_metadata = match std::fs::symlink_metadata(target) {
Ok(metadata) => Some(metadata),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => None,
Err(error) => return Err(anyhow::anyhow!("inspect capsule materialization: {error}")),
};
if let Some(metadata) = target_metadata {
if metadata.file_type().is_symlink() || !metadata.is_dir() {
anyhow::bail!("capsule materialization target is redirected or not a directory");
}
if let Ok(bound_manifest) =
astrid_capsule::discovery::load_manifest(&target.join("Capsule.toml"))
&& self
.verify_published_materialization(target, principal, &bound_manifest, snapshot)
.is_ok()
{
return Ok(bound_manifest);
}
astrid_core::platform_fs::verify_no_redirects(target).map_err(|error| {
anyhow::anyhow!("capsule materialization target is redirected: {error}")
})?;
std::fs::remove_dir_all(target).map_err(|error| {
anyhow::anyhow!("remove stale capsule materialization: {error}")View on GitHub (pinned to affd8760f4)
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.
Example fix
// before: repair fails because the cache dir is owned by root let m = kernel.ensure_published_materialization(&target, &principal, &manifest, &snapshot)?; // after: restore ownership, then repair // $ sudo chown -R <principal-uid>:<principal-gid> /path/to/cache/target let m = kernel.ensure_published_materialization(&target, &principal, &manifest, &snapshot)?;
Defensive patterns
Strategy: try-catch
Validate before calling
fn target_stattable(target: &std::path::Path) -> Result<bool, String> {
match std::fs::symlink_metadata(target) {
Ok(_) => Ok(true),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(false),
Err(e) => Err(format!("target not inspectable: {e}")),
}
} Type guard
fn is_inspectable_dir(target: &std::path::Path) -> bool {
std::fs::symlink_metadata(target)
.map(|md| !md.file_type().is_symlink() && md.is_dir())
.unwrap_or(false)
} Try / catch
match kernel.ensure_published_materialization(&target, &principal, &manifest, &snapshot) {
Ok(m) => /* proceed */,
Err(e) if e.to_string().contains("inspect capsule materialization") => {
// stat failed: check mount/permissions, or remove broken target so
// repair can take the NotFound path
let _ = std::fs::remove_dir_all(&target);
let m = kernel.ensure_published_materialization(&target, &principal, &manifest, &snapshot)?;
}
Err(e) => return Err(e),
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
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 metadata: {error:#}
- read materialized capsule authority: {error:#}
- read materialized capsule member {relative}: {error}
- capsule materialization destination is not a directory
- capsule materialization destination already exists
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/15d64c508490962d.
Report an issue: GitHub.