{"record":{"id":"b6f35bc83806603e","repo":"jdx/mise","slug":"local-task-cache-manifest-does-not-match-remote-ac","errorCode":null,"errorMessage":"local task cache manifest does not match remote action","messagePattern":"local task cache manifest does not match remote action","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/task/task_cache_store.rs","lineNumber":453,"sourceCode":"        })\n    }\n\n    async fn commit(\n        &self,\n        key: &str,\n        action: &[u8],\n        write: &TaskCacheStoreWrite,\n        manifest: &[u8],\n        has_artifact: bool,\n    ) -> Result<()> {\n        validate_remote_key(key)?;\n        let action_digest = CacheDigest::blake3(action);\n        if action_digest.hash != key {\n            bail!(\"remote cache action bytes do not match cache key\");\n        }\n        let manifest: CacheManifest = serde_json::from_slice(manifest)?;\n        if manifest.key != key {\n            bail!(\"local task cache manifest does not match remote action\");\n        }\n        let metadata = canonical_json(&serde_json::to_value(\n            RemoteClientMetadata::from_manifest(&manifest),\n        )?)?;\n        let mut uploads = vec![\n            BlobUpload {\n                digest: action_digest.clone(),\n                source: BlobSource::Bytes(action.to_vec()),\n            },\n            BlobUpload {\n                digest: CacheDigest::blake3(&metadata),\n                source: BlobSource::Bytes(metadata),\n            },\n        ];\n        let metadata = uploads[1].digest.clone();\n        let output_root = if has_artifact {\n            let (root, mut artifact_uploads) =\n                archive_to_cas(write.artifact_path(), &self.staging_dir)?;","sourceCodeStart":435,"sourceCodeEnd":471,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/task/task_cache_store.rs#L435-L471","documentation":"During commit, the CacheManifest's embedded key did not match the remote cache key the entry is being written under. The manifest is the authoritative record of what the entry contains; a mismatch means the caller is associating a manifest from a different action with this key, which would serve wrong cached results.","triggerScenarios":"Calling commit() passing a manifest serialized for one action/key together with a key (and action bytes) from another — e.g. re-serializing the manifest after changing its key field, mixing up manifests when committing several entries in a loop, or loading a manifest from disk that belongs to a different task.","commonSituations":"Batch publish scripts that zip keys[] with manifests[] from different task runs; retry logic that rebuilds the action (new key) but reuses the old manifest bytes; copying manifest files between cache entries.","solutions":["Rebuild the CacheManifest from the same action/state used to derive the key, set manifest.key = key, and re-serialize immediately before commit","Verify each (key, action, manifest) triple is built from one run — don't zip mismatched arrays in batch commits","Deserialize the manifest and compare its key to the intended key before calling commit to fail early","Clear stale manifests and regenerate rather than reusing previously serialized bytes"],"exampleFix":"// before\nlet manifest_bytes = old_manifest_bytes; // key from previous run\nstore.commit(&key, &action, &manifest_bytes, true)?;\n// after\nlet mut manifest = build_manifest(&action);\nmanifest.key = key;\nstore.commit(&key, &action, &serde_json::to_vec(&manifest)?, true)?;","handlingStrategy":"validation","validationCode":"let manifest: CacheManifest = serde_json::from_slice(&manifest_bytes)?;\nanyhow::ensure!(manifest.key == key, \"manifest key {} != commit key {}\", manifest.key, key);\nstore.commit(&key, &action, &manifest_bytes, has_artifact)?;","typeGuard":null,"tryCatchPattern":"match store.commit(&key, &action, &manifest_bytes, has_artifact).await {\n    Err(e) if e.to_string().contains(\"manifest does not match remote action\") => {\n        let mut m = rebuild_manifest(&action);\n        m.key = key;\n        store.commit(&key, &action, &serde_json::to_vec(&m)?, has_artifact).await\n    }\n    other => other,\n}","preventionTips":["Build key, action, and manifest together from a single task run","Set manifest.key from the same value passed as the commit key","In batch publishing, assert triple consistency (key/action/manifest) per entry before committing"],"tags":["cache","integrity","content-addressed-storage","key-mismatch"],"backgroundTag":"checksum-mismatch","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}