{"record":{"id":"21fb8e8b3986d094","repo":"jdx/mise","slug":"cannot-publish-an-action-result-with-a-missing-blo","errorCode":null,"errorMessage":"cannot publish an action result with a missing blob","messagePattern":"cannot publish an action result with a missing blob","errorType":"exception","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"crates/mise-cache-core/src/local.rs","lineNumber":215,"sourceCode":"        }\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() {\n                bail!(\"cannot publish an action result with a missing blob\");\n            }\n        }\n        let destination = self.path_for(&result.action)?;\n        let replace_invalid = match self.find(&result.action) {\n            Ok(Some(existing)) => {\n                if existing == *result {\n                    return Ok(destination);\n                }\n                bail!(\"local action key already has a different result\");\n            }\n            Ok(None) => false,\n            Err(_) => true,\n        };\n        let parent = destination\n            .parent()\n            .expect(\"action-result path has a parent\");\n        fs::create_dir_all(parent)?;\n        let mut temporary = tempfile::NamedTempFile::new_in(parent)?;","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/crates/mise-cache-core/src/local.rs#L197-L233","documentation":"`LocalActionCache::store` requires every digest referenced by the result - the action digest itself plus optional `metadata` and `output_root` - to already exist and verify in the local CAS before the index entry is published. Publishing without the blobs would create dangling references. The crate's own test (`publishes_action_results_after_referenced_blobs`) documents the required order: blobs first, action result second.","triggerScenarios":"Calling `actions.store(&result)` before `cas.store_bytes`/`store_file` for the action digest, the metadata digest, or the output-root digest. A common miss is forgetting that the action key blob itself must be present in the CAS, not just the outputs.","commonSituations":"Publishing only outputs and skipping the action manifest blob; download flows persisting an action result before materializing its blobs into the local CAS.","solutions":["Store all referenced blobs first: `cas.store_bytes(&result.action, ...)`, plus metadata and output_root when present, then `actions.store(&result)`.","When publishing downloaded results, materialize blobs into the local CAS before storing the action entry.","As a pre-check, iterate `[result.action, result.metadata, result.output_root]` and confirm `cas.find` returns each."],"exampleFix":"// before\nactions.store(&result)?; // bails: referenced blobs missing\n\n// after\ncas.store_bytes(&result.action, &manifest_bytes)?;\nif let Some(d) = &result.metadata { cas.store_bytes(d, &metadata_bytes)?; }\nif let Some(d) = &result.output_root { cas.store_file(d, &output_root_dir)?; }\nactions.store(&result)?;","handlingStrategy":"validation","validationCode":"let refs = [Some(&result.action), result.metadata.as_ref(), result.output_root.as_ref()]\n    .into_iter()\n    .flatten();\nfor digest in refs {\n    if cas.find(digest)?.is_none() {\n        cas.store_bytes(digest, &materialize(digest).await?)?;\n    }\n}\nactions.store(&result)?;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always publish blobs before action results; the action manifest blob is required too.","Centralize the publish sequence in one function so the order cannot be inverted.","On download paths, materialize all referenced blobs into the local CAS before storing the index entry."],"tags":["local-cache","action-cache","cas","publish-order","rust"],"backgroundTag":"missing-cache-blob","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}