jdx/mise · error

cached OCI layer digest mismatch

Error message

cached OCI layer digest mismatch

What it means

While validating a cached OCI layer in read_cached_layer (src/oci/layer/cache.rs:131), mise recomputes the SHA-256 of the gzipped layer blob on disk and compares it to the digest recorded in the cache metadata. This error means the content hash does not match `record.digest`: the blob file was modified, corrupted, or swapped after being cached, even though its size happened to match.

Solutions

  1. Delete the affected cached layer (file named <digest> in the OCI cache dir) — or the whole OCI cache — and let mise re-download/rebuild it.
  2. Re-run the layer-producing operation online so the cache is repopulated from the trusted source.
  3. Investigate disk/filesystem health if multiple entries fail validation (memory or disk corruption).
  4. Avoid manual writes into the OCI cache and don't share one cache dir concurrently across independent builds.

Example fix

# before: digest mismatch on layer sha256:abc...
rm ~/.cache/mise/oci/layers/sha256:abc...
# after: layer re-fetched and validated
mise build-push ...
Defensive patterns

Strategy: fallback

Validate before calling

let bytes = std::fs::read(blob_path)?;
let actual = Sha256::digest(&bytes);
if format!("sha256:{x}", x = hex::encode(actual)) != record.digest {
  eprintln!("cache digest mismatch; invalidating");
  std::fs::remove_file(blob_path)?;
}

Try / catch

match read_cached_layer(...) {
  Err(e) if e.to_string().contains("digest mismatch") => {
    invalidate_cache_entry(digest);
    fetch_layer_fresh(digest)
  }
  other => other,
}

Prevention

When it happens

Trigger: Calling read_cached_layer (via build_cached_tool_layer) where bytes.len() == record.size but sha256(bytes) != record.digest. Causes include bit-rot/corruption, in-place tampering or manual edits, a hash-collision-shaped bug (overwritten blob of identical size from another layer), or unsafe shared-cache writes.

Common situations: Cache directories shared across machines or manipulated by other tools; disk corruption where file size survives but content changed; manually 'fixing' a cache entry by copying a different layer file in; concurrent builds racing on the same cache path.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/23c6ec42dc306f6b. Report an issue: GitHub.

Appendix: source

Thrown at src/oci/layer/cache.rs:131

    hash.update(bytes);
}

fn read_cached_layer(record_path: &Path, cache_dir: &Path) -> Result<Option<LayerBlob>> {
    let record = match std::fs::read(record_path) {
        Ok(bytes) => bytes,
        Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(None),
        Err(err) => return Err(err.into()),
    };
    let record: CachedLayer = serde_json::from_slice(&record)?;
    crate::oci::layout::validate_sha256_digest(&record.digest)?;
    crate::oci::layout::validate_sha256_digest(&record.diff_id)?;
    let bytes = std::fs::read(cache_dir.join(record.digest.trim_start_matches("sha256:")))?;
    eyre::ensure!(
        bytes.len() as u64 == record.size,
        "cached OCI layer size mismatch"
    );
    let digest = format!("sha256:{}", hex_encode(&Sha256::digest(&bytes)));
    eyre::ensure!(digest == record.digest, "cached OCI layer digest mismatch");
    let mut decoder = flate2::read::GzDecoder::new(bytes.as_slice());
    let mut hash = Sha256::new();
    let mut buffer = [0; 64 * 1024];
    loop {
        let n = decoder.read(&mut buffer)?;
        if n == 0 {
            break;
        }
        hash.update(&buffer[..n]);
    }
    let diff_id = format!("sha256:{}", hex_encode(&hash.finalize()));
    eyre::ensure!(
        diff_id == record.diff_id,
        "cached OCI layer diff ID mismatch"
    );
    Ok(Some(LayerBlob {
        digest,
        diff_id,

View on GitHub (pinned to 533346cc37)