astrid-runtime/astrid · error

capsule cache path does not contain owner/id/digest…

Error message

capsule cache path does not contain owner/id/digest components

What it means

This error means the capsule cache directory layout under the cache root did not have exactly three path components (owner/id/digest), so the kernel could not verify the cache entry against the authenticated principal and manifest. The library throws it while validating a materialized capsule's cache location, since the owner-uid/package-name/digest structure is required to attribute and integrity-check cached packages.

Solutions

  1. Clear the capsule cache directory and re-materialize the capsule so the kernel recreates the owner/id/digest layout
  2. Ensure the kernel/cache being used is the same version that wrote the entries (mixed-version cache layouts cause this)
  3. Do not hand-move or rename cache subdirectories; let the kernel manage cache layout
  4. If a script seeds the cache, write entries as <cache-root>/<owner-uid>/<package-name>/<digest>/

Example fix

// before: script seeding cache with wrong layout
fs::copy(archive, cache_root.join("mypackage"))?;

// after: owner-uid/package-name/digest layout
let uid = kernel.principal_directory().uid_for(&principal)?;
let digest = blake3::hash(&archive).to_hex().to_string();
let dir = cache_root.join(uid.to_string()).join("mypackage").join(digest);
fs::create_dir_all(&dir)?;
fs::copy(archive, dir.join("package.capsule"))?;
Defensive patterns

Strategy: validation

Validate before calling

let rel = cache_entry.strip_prefix(cache_root)?;
if rel.components().count() != 3 {
    return Err("cache entry must be <owner-uid>/<id>/<digest>");
}

Try / catch

match result {
    Err(e) if e.to_string().contains("owner/id/digest components") => {
        // invalidate the cache entry and re-materialize
    }
    other => other?,
}

Prevention

When it happens

Trigger: Invoking the cache-path validation (during materialization from a registry snapshot) with a cache directory whose path, relative to the cache root, yields fewer or more than 3 components — e.g. a cache entry written by an older kernel version with a flat layout, a manually moved/renamed cache dir, or a path passed directly instead of the expected owner/id/digest leaf.

Common situations: Upgrading from an older kernel whose cache layout differed; manually cleaning or restructuring the cache directory by hand; running two kernel versions against a shared cache; a script copying cache entries to the wrong depth.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/8fa24be5d2712933. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-kernel/src/lib.rs:1597

        snapshot: &astrid_storage::CapsulePackageSnapshot,
    ) -> anyhow::Result<()> {
        let cache_root = self.astrid_home.run_dir().join("capsules");
        let relative = dir.strip_prefix(&cache_root).map_err(|_| {
            anyhow::anyhow!("capsule cache path is outside the durable registry cache")
        })?;
        astrid_core::platform_fs::verify_no_redirects(dir)
            .map_err(|error| anyhow::anyhow!("capsule cache path is redirected: {error}"))?;
        let components: Vec<String> = relative
            .components()
            .map(|component| match component {
                std::path::Component::Normal(value) => Ok(value.to_string_lossy().into_owned()),
                _ => Err(anyhow::anyhow!(
                    "capsule cache path contains unsafe components"
                )),
            })
            .collect::<anyhow::Result<_>>()?;
        if components.len() != 3 {
            anyhow::bail!("capsule cache path does not contain owner/id/digest components");
        }
        let uid = self
            .principal_directory
            .uid_for(principal)
            .map_err(|error| anyhow::anyhow!("resolve capsule cache owner UID: {error}"))?;
        if components[0] != uid.to_string() || components[1] != manifest.package.name {
            anyhow::bail!("capsule cache owner or id does not match authenticated registry scope");
        }
        let digest = blake3::hash(&snapshot.package().archive)
            .to_hex()
            .to_string();
        if components[2] != digest {
            anyhow::bail!("materialized capsule digest does not match durable registry");
        }
        Ok(())
    }

    /// Inventory a projection without traversing redirects or special files.

View on GitHub (pinned to affd8760f4)