{"record":{"id":"e7a8af3cb1e7554c","repo":"jdx/mise","slug":"local-action-result-is-invalid","errorCode":null,"errorMessage":"local action result is invalid: {}","messagePattern":"local action result is invalid: (.+?)","errorType":"exception","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"crates/mise-cache-core/src/local.rs","lineNumber":196,"sourceCode":"        }\n        Ok(self\n            .root\n            .join(\"action-results/v1\")\n            .join(&action.algorithm)\n            .join(&action.hash[..2])\n            .join(format!(\"{}-{}.json\", action.hash, action.size)))\n    }\n\n    /// Find and strictly validate a canonical action result.\n    pub fn find(&self, action: &CacheDigest) -> Result<Option<RemoteActionResult>> {\n        let path = self.path_for(action)?;\n        if !path.exists() {\n            return Ok(None);\n        }\n        let bytes = fs::read(&path)?;\n        let result: RemoteActionResult = serde_json::from_slice(&bytes)?;\n        if result.version != 1 || result.action != *action || canonical_json(&result)? != bytes {\n            bail!(\"local action result is invalid: {}\", path.display());\n        }\n        Ok(Some(result))\n    }\n\n    /// Atomically publish an action result after validating all referenced objects.\n    pub fn store(&self, result: &RemoteActionResult) -> Result<PathBuf> {\n        if result.version != 1 {\n            bail!(\"unsupported local action result version\");\n        }\n        for digest in [\n            Some(&result.action),\n            result.metadata.as_ref(),\n            result.output_root.as_ref(),\n        ]\n        .into_iter()\n        .flatten()\n        {\n            if self.cas.find(digest)?.is_none() {","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/crates/mise-cache-core/src/local.rs#L178-L214","documentation":"LocalActionCache::find strictly validates a stored action-result file: version must equal 1, the record's action field must equal the requested digest, and the file bytes must exactly equal canonical_json of the parsed record. Any deviation — truncation, pretty-printing, a stale format, or a record stored under the wrong key — is treated as corruption and the offending path is included in the message.","triggerScenarios":"A JSON file under action-results/v1/ that was hand-edited or reformatted (breaking canonical byte equality); truncation from a crash in an older non-atomic writer; files written by a previous version with different canonicalization or version; a result written under another action's key.","commonSituations":"Developers or formatters 'normalizing' JSON inside the cache directory; power loss during writes before atomic persist existed; upgrading mise/cache tooling across format versions; tools that reserialize JSON on save.","solutions":["Delete the offending .json file named in the error — LocalActionCache::store automatically replaces invalid entries on the next publish","If many entries fail, clear the action-results directory (or the whole cache root) and repopulate from rebuilds","Keep editors, formatters, and sync tools away from the cache root","After upgrades, treat the old cache as disposable instead of migrating files by hand"],"exampleFix":"# before: actions.find(&action) fails with\n#   local action result is invalid: /home/u/.cache/mise/action-results/v1/blake3/ab/abcd1234…-57.json\n\n# after: remove the corrupt record (store() rewrites it on the next publish)\nrm /home/u/.cache/mise/action-results/v1/blake3/ab/abcd1234…-57.json","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"let result = match actions.find(&action) {\n    Ok(result) => result,\n    Err(report) if report.to_string().contains(\"local action result is invalid\") => {\n        // corrupt record: delete it, treat as a miss; store() rewrites it on next publish\n        let _ = std::fs::remove_file(actions.path_for(&action)?);\n        None\n    }\n    Err(report) => return Err(report),\n};","preventionTips":["Never edit, format, or migrate files under the cache root by hand","Note that LocalActionCache::store transparently replaces invalid entries — prefer re-storing over manual surgery","After format-changing upgrades, clear the action-results directory rather than converting it"],"tags":["local-cache","action-result","corruption","canonical-json"],"backgroundTag":"cache-corruption","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}