{"record":{"id":"d914e632aeaed006","repo":"jdx/mise","slug":"remote-action-output-directory-is-invalid","errorCode":null,"errorMessage":"remote action output directory is invalid","messagePattern":"remote action output directory is invalid","errorType":"exception","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"crates/mise-cache-core/src/agent.rs","lineNumber":1205,"sourceCode":"        }\n        Ok(path)\n    }\n\n    fn parse_rustc_metadata(path: &Path) -> Result<RustcMetadata> {\n        let bytes = fs::read(path)?;\n        let metadata: RustcMetadata = serde_json::from_slice(&bytes)?;\n        if metadata.version != 1 || metadata.kind != \"rustc\" || canonical_json(&metadata)? != bytes\n        {\n            bail!(\"remote rustc action metadata is invalid\");\n        }\n        Ok(metadata)\n    }\n\n    fn parse_cache_directory(path: &Path) -> Result<CacheDirectory> {\n        let bytes = fs::read(path)?;\n        let directory: CacheDirectory = serde_json::from_slice(&bytes)?;\n        if directory.version != 1 || canonical_json(&directory)? != bytes {\n            bail!(\"remote action output directory is invalid\");\n        }\n        Ok(directory)\n    }\n\n    #[cfg(test)]\n    fn load_cache_directory(&self, digest: &CacheDigest) -> Result<CacheDirectory> {\n        let path = self\n            .find_verified_blob(digest)?\n            .ok_or_else(|| eyre::eyre!(\"remote action output directory is missing\"))?;\n        Self::parse_cache_directory(&path)\n    }\n\n    fn validate_prefetched_action(\n        action: &PrefetchedAction,\n        verified: &BTreeMap<CacheDigest, PathBuf>,\n        rustc_metadata: &BTreeMap<CacheDigest, RustcMetadata>,\n        directories: &BTreeMap<CacheDigest, CacheDirectory>,\n    ) -> Result<()> {","sourceCodeStart":1187,"sourceCodeEnd":1223,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/crates/mise-cache-core/src/agent.rs#L1187-L1223","documentation":"parse_cache_directory validates a fetched output-directory object: it must deserialize as CacheDirectory, carry version==1, and its bytes must equal the canonical JSON re-serialization. Directory objects describe the output DAG of a cached action, so tampered or schema-mismatched bytes are rejected.","triggerScenarios":"Remote cache serving a directory object from an incompatible writer version, a hand-edited object, or JSON that parses but was not written in canonical form.","commonSituations":"Shared remote bucket written by mixed agent versions; external tooling reformatting cache objects; interrupted uploads.","solutions":["Delete/overwrite the invalid directory blob so the owning action is re-executed and re-stored","Align all writers and readers on the same mise-cache-core version","Treat repeated failures as a corrupt remote entry: remove the whole action manifest entry for that task"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"if let Err(e) = restore_prefetched(&agent).await {\n    if e.to_string().contains(\"remote action output directory is invalid\") {\n        evict_remote_blobs_for_action(&digest).await?;\n        run_action_locally(&action).await?; // rebuild and re-store\n    } else { return Err(e); }\n}","preventionTips":["Keep agent versions uniform so CacheDirectory schema (version 1) stays consistent","Use immutable, digest-addressed uploads so partial writes cannot be observed","Treat integrity errors as eviction triggers, not as fatal state"],"tags":["mise-cache","cache-corruption","json","validation"],"backgroundTag":"cache-corruption","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}