{"record":{"id":"9528114518917970","repo":"jdx/mise","slug":"cached-oci-layer-diff-id-mismatch","errorCode":null,"errorMessage":"cached OCI layer diff ID mismatch","messagePattern":"cached OCI layer diff ID mismatch","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/oci/layer/cache.rs","lineNumber":143,"sourceCode":"    let bytes = std::fs::read(cache_dir.join(record.digest.trim_start_matches(\"sha256:\")))?;\n    eyre::ensure!(\n        bytes.len() as u64 == record.size,\n        \"cached OCI layer size mismatch\"\n    );\n    let digest = format!(\"sha256:{}\", hex_encode(&Sha256::digest(&bytes)));\n    eyre::ensure!(digest == record.digest, \"cached OCI layer digest mismatch\");\n    let mut decoder = flate2::read::GzDecoder::new(bytes.as_slice());\n    let mut hash = Sha256::new();\n    let mut buffer = [0; 64 * 1024];\n    loop {\n        let n = decoder.read(&mut buffer)?;\n        if n == 0 {\n            break;\n        }\n        hash.update(&buffer[..n]);\n    }\n    let diff_id = format!(\"sha256:{}\", hex_encode(&hash.finalize()));\n    eyre::ensure!(\n        diff_id == record.diff_id,\n        \"cached OCI layer diff ID mismatch\"\n    );\n    Ok(Some(LayerBlob {\n        digest,\n        diff_id,\n        size: record.size,\n        bytes,\n    }))\n}\n\nfn write_cached_layer(record_path: &Path, cache_dir: &Path, blob: &LayerBlob) -> Result<()> {\n    crate::file::create_dir_all(cache_dir)?;\n    let blob_path = cache_dir.join(blob.digest.trim_start_matches(\"sha256:\"));\n    // Atomic blob publication followed by atomic metadata publication lets\n    // concurrent builds share this cache without sharing an image index.\n    crate::file::write_atomic(blob_path, &blob.bytes)?;\n    crate::file::write_atomic(","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/oci/layer/cache.rs#L125-L161","documentation":"After decompressing a cached OCI layer (read_cached_layer, src/oci/layer/cache.rs:143), mise hashes the uncompressed content and compares it to the recorded diff_id (the digest of the uncompressed tar). This error means the gunzipped layer content does not match the cached `diff_id` — the compressed blob may be valid gzip but not the layer originally recorded, or the cache record is stale/wrong.","triggerScenarios":"Calling read_cached_layer (via build_cached_tool_layer) where the gzipped blob passes digest validation but SHA-256 of its decompressed stream != record.diff_id. Causes: cache metadata written for a different layer version, corruption inside the gzip stream that still decompresses, or a registry/layout mismatch between digest and diff_id at cache-write time.","commonSituations":"Cache entries created by an older mise version with a differing layer build; tool image rebuilt upstream with the same tag so the cached diff_id no longer matches; corrupted multi-stream gzip; race between cache write and read.","solutions":["Invalidate the OCI layer cache (delete the cache dir or the specific layer + record) and re-run so the layer is re-fetched with fresh metadata.","Rebuild the tool layer from source instead of using the cache if the layer was produced locally.","Update mise — earlier versions could write inconsistent digest/diff_id records; the current code validates both on read.","Check that the source OCI layout/registry the cache came from is not serving mutated layers under the same digest."],"exampleFix":"# before: diff ID mismatch during cached build\nrm -rf ~/.cache/mise/oci\n# after\nmise build-push ...  # re-extracts layer and recomputes diff_id","handlingStrategy":"fallback","validationCode":"// after decompressing, verify before use\nlet diff_id = format!(\"sha256:{}\", hex_encode(Sha256::digest(&uncompressed)));\nif diff_id != record.diff_id {\n  eprintln!(\"cached diff_id stale; rebuilding layer\");\n  rebuild_layer(image_dir)?;\n}","typeGuard":null,"tryCatchPattern":"match read_cached_layer(...) {\n  Err(e) if e.to_string().contains(\"diff ID mismatch\") => {\n    invalidate_cache_entry(record.digest);\n    rebuild_or_refetch_layer(image_dir)\n  }\n  other => other,\n}","preventionTips":["Keep mise updated so cache records are written consistently","Clear the OCI cache after upgrading tools/images upstream","Rebuild layers locally after registry image mutations rather than trusting stale cache"],"tags":["oci","cache","integrity","diff-id"],"backgroundTag":"checksum-mismatch","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}