astrid-runtime/astrid · error

capsule cache owner or id does not match authenticated…

Error message

capsule cache owner or id does not match authenticated registry scope

What it means

This error means the cache directory's owner (uid) or package id component did not match the currently authenticated principal's uid or the manifest's package name. The kernel throws it to enforce that a capsule cache entry can only be used by the registry identity that owns it — preventing a capsule from being materialized out of another principal's cache slot (cross-tenant cache confusion).

Solutions

  1. Clear the capsule cache (or the specific owner/id entry) and re-materialize under the current authenticated principal
  2. Verify the authenticated principal is the one that originally populated the cache; log in with the intended registry identity
  3. Ensure the manifest's package.name matches the cache directory's id component — fix the manifest or the cache dir to agree
  4. In shared/CI environments, scope the cache per-principal (e.g. include uid in the cache key) instead of sharing one cache root

Example fix

// before: shared cache across principals
let cache_root = Path::new("/var/cache/astrid/capsules");

// after: per-principal cache scope
let uid = kernel.principal_directory().uid_for(&principal)?;
let cache_root = Path::new("/var/cache/astrid/capsules").join(uid.to_string());
Defensive patterns

Strategy: validation

Validate before calling

let uid = kernel.principal_directory().uid_for(&principal)?;
if !cache_entry.starts_with(cache_root.join(uid.to_string())) {
    return Err("cache entry belongs to a different principal");
}

Try / catch

match result {
    Err(e) if e.to_string().contains("authenticated registry scope") => {
        // re-authenticate as the owning principal or purge the cache entry
    }
    other => other?,
}

Prevention

When it happens

Trigger: The cache path validation finds components[0] != uid_for(principal) or components[1] != manifest.package.name — e.g. authenticating as a different user than the one whose cache was populated, or loading a manifest whose package name differs from the cache directory name.

Common situations: Switching registry accounts/identities while reusing a shared cache directory; renaming a package in Capsule.toml without clearing the cache; running the kernel under a different OS user (different uid) on a shared machine; CI reusing a cached volume from a job with a different principal.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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

Appendix: source

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

            .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.
    #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
    fn inventory_projection_files(root: &Path) -> anyhow::Result<ProjectionInventory> {
        fn walk(
            root: &Path,
            directory: &Path,
            inventory: &mut ProjectionInventory,
        ) -> anyhow::Result<()> {

View on GitHub (pinned to affd8760f4)