jdx/mise · error

blob digest mismatch: got {actual}, expected {digest}

Error message

blob digest mismatch: got {actual}, expected {digest}

What it means

`write_blob_with_digest` in src/oci/layout.rs verifies that the blob bytes it is about to write hash (SHA-256) to the digest the caller claims. If the computed digest differs from `digest`, it refuses to write, protecting the OCI layout from corrupt or substituted content pulled from a registry.

Source

Thrown at src/oci/layout.rs:71

    /// Copy a blob into the layout by its known digest.
    ///
    /// Two guards:
    ///  1. The digest must be `sha256:` followed by 64 lowercase hex chars.
    ///     This prevents path traversal (e.g. a malicious registry returning
    ///     `sha256:../../etc/passwd` would otherwise let us write attacker-
    ///     controlled bytes to an arbitrary filesystem path).
    ///  2. We verify `sha256(bytes) == digest` before writing so corrupted
    ///     or tampered content surfaces with a clear "got X, wanted Y"
    ///     message instead of later as a confusing mismatch from skopeo /
    ///     podman.
    pub(crate) fn write_blob_with_digest(&self, digest: &str, bytes: &[u8]) -> Result<()> {
        validate_sha256_digest(digest)?;
        let mut h = Sha256::new();
        h.update(bytes);
        let actual = format!("sha256:{}", crate::oci::layer::hex_encode(&h.finalize()));
        if actual != digest {
            eyre::bail!("blob digest mismatch: got {actual}, expected {digest}");
        }
        let path = self.blob_path(digest);
        if !path.exists() {
            file::write(&path, bytes)?;
        }
        Ok(())
    }

    pub(crate) fn blob_path(&self, digest: &str) -> PathBuf {
        let hex = digest.trim_start_matches("sha256:");
        self.root.join("blobs/sha256").join(hex)
    }

    pub(crate) fn read_blob(&self, digest: &str) -> Result<Vec<u8>> {
        // Validate before turning the digest into a path component — a crafted
        // layout (`mise oci push/run --image-dir <untrusted>`) could otherwise
        // use `sha256:../../etc/passwd` to read outside the blobs directory.
        validate_sha256_digest(digest)?;

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Re-pull the blob from the registry (delete any partially cached content first) and retry
  2. Verify the digest you pass matches the blob you downloaded (recompute sha256 over the exact bytes received)
  3. Check the registry/CDN for corruption; try a different mirror or proxy
  4. Fix caller bookkeeping so each digest is paired with its own bytes

Example fix

// before
layout.write_blob_with_digest(layer_digest, &manifest_bytes)?; // wrong bytes for this digest
// after
let blob_bytes = registry.fetch_blob(layer_digest).await?;
layout.write_blob_with_digest(&layer_digest, &blob_bytes)?;
Defensive patterns

Strategy: validation

Validate before calling

fn sha256_hex(bytes: &[u8]) -> String {
    let mut h = Sha256::new(); h.update(bytes);
    format!("sha256:{}", hex::encode(h.finalize()))
}
assert_eq!(sha256_hex(&bytes), digest, "refusing to write mismatched blob");

Try / catch

match layout.write_blob_with_digest(&digest, &bytes) {
    Ok(()) => /* ... */,
    Err(e) if e.to_string().contains("digest mismatch") => {
        // discard cached bytes and re-pull from registry
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `write_blob_with_digest(digest, bytes)` where `bytes` do not hash to `digest` — e.g. a registry returned a different blob than advertised, the download was truncated/corrupted, or the caller mixed up blobs (wrote layer bytes under the manifest's digest).

Common situations: `pull_base_image` downloading layers over a flaky/middleboxed network; a compromised or misbehaving registry returning wrong content; CDN caching bugs; caller-side bookkeeping bugs passing the wrong digest for the bytes.

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@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/744fd1520f4da4e7. Report an issue: GitHub.