jdx/mise · error

remote cache blob pack returned an unrequested digest

Error message

remote cache blob pack returned an unrequested digest

What it means

Thrown while decoding a blob pack streamed from the remote build cache. For every entry, mise-cache-core reads the framing header (one algorithm byte, a 32-byte hash, an 8-byte big-endian size), rebuilds a CacheDigest, and requires it to be one of the digests that were POSTed in the DigestList request body. If the server includes a blob that was never requested, decoding aborts immediately so unrequested content never reaches the local content-addressed store. This is a server-contract violation: the blob pack endpoint may only return blobs from the request list.

Source

Thrown at crates/mise-cache-core/src/lib.rs:930

        let (algorithm, mut hasher) = match algorithm[0] {
            1 => (
                "blake3",
                BlobPackHasher::Blake3(Box::new(blake3::Hasher::new())),
            ),
            2 => ("sha256", BlobPackHasher::Sha256(sha2::Sha256::new())),
            _ => bail!("remote cache blob pack has an invalid digest algorithm"),
        };
        let mut hash = [0_u8; 32];
        reader.read_exact(&mut hash).await?;
        let mut size = [0_u8; 8];
        reader.read_exact(&mut size).await?;
        let digest = CacheDigest {
            algorithm: algorithm.into(),
            hash: hex::encode(hash),
            size: u64::from_be_bytes(size),
        };
        if !requested.contains(&digest) {
            bail!("remote cache blob pack returned an unrequested digest");
        }
        if !seen.insert(digest.clone()) {
            bail!("remote cache blob pack returned a duplicate digest");
        }
        framed_bytes = framed_bytes
            .checked_add(BLOB_PACK_HEADER_BYTES)
            .and_then(|bytes| bytes.checked_add(digest.size))
            .ok_or_else(|| eyre!("remote cache blob pack is too large"))?;
        payload_bytes = payload_bytes
            .checked_add(digest.size)
            .ok_or_else(|| eyre!("remote cache blob pack payload is too large"))?;

        let path = directory.path().join(blobs.len().to_string());
        let mut output = tokio::fs::File::create(&path).await?;
        let mut remaining = digest.size;
        let mut buffer = [0_u8; 64 * 1024];
        while remaining > 0 {
            let limit = usize::try_from(remaining.min(buffer.len() as u64)).unwrap();

View on GitHub (pinned to 6f52dcdf99)

Solutions

  1. Verify the cache server implements the pack contract: iterate only the requested digests and frame each entry as algorithm byte + 32-byte hash + 8-byte big-endian size after the magic header.
  2. Disable POST-response caching on any proxy/CDN in front of the blob pack endpoint.
  3. Capture the request DigestList body and the response stream, then diff entry digests against the request to identify the unrequested entry.
  4. If the server is third-party, pin client and server to versions tested together, or make the pack endpoint return 404/405/501 so the client falls back to per-blob GETs.
  5. As a last resort, clear the affected cache entries so the pack is rebuilt from a known-good state.
Defensive patterns

Strategy: try-catch

Try / catch

let pack = match remote_cache.download(staging_dir).await {
    Ok(pack) => pack,
    Err(err) if err.to_string().contains("blob pack") => {
        // server violated the pack contract; treat as a cache miss
        warn!("invalid blob pack: {err}");
        return recompute_locally();
    }
    Err(err) => return Err(err),
};

Prevention

When it happens

Trigger: POSTing a digest list to the blob pack endpoint (the remote cache's missing-blob download after an action cache hit) and the response stream contains an entry whose (algorithm, hash, size) triple is not in that list. Typical producers: a server that assembles the pack from its own inventory instead of the request, a proxy/CDN serving a cached POST response from a different request, or a pack-format framing mismatch between client and server versions.

Common situations: Running a custom or third-party cache server that ignores the request digest list; an HTTP proxy that reuses cached POST responses; client/server version skew where entry headers are framed differently so digests get misparsed mid-stream.

Related errors


AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22). Data as JSON: /api/errors/43c5243a0e298c8a. Report an issue: GitHub.