{"record":{"id":"8fa24be5d2712933","repo":"astrid-runtime/astrid","slug":"capsule-cache-path-does-not-contain-owner-id-diges","errorCode":null,"errorMessage":"capsule cache path does not contain owner/id/digest components","messagePattern":"capsule cache path does not contain owner/id/digest components","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/lib.rs","lineNumber":1597,"sourceCode":"        snapshot: &astrid_storage::CapsulePackageSnapshot,\n    ) -> anyhow::Result<()> {\n        let cache_root = self.astrid_home.run_dir().join(\"capsules\");\n        let relative = dir.strip_prefix(&cache_root).map_err(|_| {\n            anyhow::anyhow!(\"capsule cache path is outside the durable registry cache\")\n        })?;\n        astrid_core::platform_fs::verify_no_redirects(dir)\n            .map_err(|error| anyhow::anyhow!(\"capsule cache path is redirected: {error}\"))?;\n        let components: Vec<String> = relative\n            .components()\n            .map(|component| match component {\n                std::path::Component::Normal(value) => Ok(value.to_string_lossy().into_owned()),\n                _ => Err(anyhow::anyhow!(\n                    \"capsule cache path contains unsafe components\"\n                )),\n            })\n            .collect::<anyhow::Result<_>>()?;\n        if components.len() != 3 {\n            anyhow::bail!(\"capsule cache path does not contain owner/id/digest components\");\n        }\n        let uid = self\n            .principal_directory\n            .uid_for(principal)\n            .map_err(|error| anyhow::anyhow!(\"resolve capsule cache owner UID: {error}\"))?;\n        if components[0] != uid.to_string() || components[1] != manifest.package.name {\n            anyhow::bail!(\"capsule cache owner or id does not match authenticated registry scope\");\n        }\n        let digest = blake3::hash(&snapshot.package().archive)\n            .to_hex()\n            .to_string();\n        if components[2] != digest {\n            anyhow::bail!(\"materialized capsule digest does not match durable registry\");\n        }\n        Ok(())\n    }\n\n    /// Inventory a projection without traversing redirects or special files.","sourceCodeStart":1579,"sourceCodeEnd":1615,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/lib.rs#L1579-L1615","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Clear the capsule cache directory and re-materialize the capsule so the kernel recreates the owner/id/digest layout","Ensure the kernel/cache being used is the same version that wrote the entries (mixed-version cache layouts cause this)","Do not hand-move or rename cache subdirectories; let the kernel manage cache layout","If a script seeds the cache, write entries as <cache-root>/<owner-uid>/<package-name>/<digest>/"],"exampleFix":"// before: script seeding cache with wrong layout\nfs::copy(archive, cache_root.join(\"mypackage\"))?;\n\n// after: owner-uid/package-name/digest layout\nlet uid = kernel.principal_directory().uid_for(&principal)?;\nlet digest = blake3::hash(&archive).to_hex().to_string();\nlet dir = cache_root.join(uid.to_string()).join(\"mypackage\").join(digest);\nfs::create_dir_all(&dir)?;\nfs::copy(archive, dir.join(\"package.capsule\"))?;","handlingStrategy":"validation","validationCode":"let rel = cache_entry.strip_prefix(cache_root)?;\nif rel.components().count() != 3 {\n    return Err(\"cache entry must be <owner-uid>/<id>/<digest>\");\n}","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"owner/id/digest components\") => {\n        // invalidate the cache entry and re-materialize\n    }\n    other => other?,\n}","preventionTips":["Let the kernel manage cache layout; never hand-move cache directories","Use one kernel version per cache directory (avoid mixed-version shared caches)","If pre-seeding cache, follow the owner-uid/package-name/digest layout exactly","Clear stale cache after kernel upgrades that change layout"],"tags":["cache","path","validation","capsule","storage-layout"],"backgroundTag":"invalid-argument-format","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}