jdx/mise · error
bytes do not match the declared CAS digest
Error message
bytes do not match the declared CAS digest
What it means
`LocalCas::store_bytes` verifies the in-memory bytes against the declared digest (both size and hash, blake3 or sha-256) before writing anything to disk. A mismatch means the digest was computed over different bytes than the ones passed, and the store refuses to publish content that would corrupt the content-addressed layout.
Solutions
- Compute the digest from the exact same slice immediately before storing: `let digest = CacheDigest::blake3(&bytes); cas.store_bytes(&digest, &bytes)`.
- When the digest comes from a manifest or remote peer, call `digest.matches_bytes(&bytes)` first and handle a mismatch explicitly instead of storing.
- Check that `digest.size` is the byte length of the content, not a human-readable size.
Example fix
// before let digest = CacheDigest::blake3(&original); cas.store_bytes(&digest, &updated)?; // bails // after let digest = CacheDigest::blake3(&updated); cas.store_bytes(&digest, &updated)?;
Defensive patterns
Strategy: validation
Validate before calling
if !digest.matches_bytes(&bytes)? {
// digest describes different content: recompute it from these bytes
let digest = CacheDigest::blake3(&bytes);
return cas.store_bytes(&digest, bytes);
}
cas.store_bytes(&digest, &bytes)?; Prevention
- Always derive the digest and the bytes from the same buffer in the same expression scope.
- Check `digest.matches_bytes` before storing when digests arrive from external sources.
- Never construct CacheDigest.size by hand; use the computed byte length.
When it happens
Trigger: Hashing one buffer but storing another: computing `CacheDigest::blake3(&old_bytes)` then passing `new_bytes` to `store_bytes`; a digest deserialized from JSON or a remote response whose `size` or `hash` does not describe the bytes at hand; mutating the buffer between hashing and storing.
Common situations: TOCTOU between digest computation and store in producer code; copy-paste mixing digest/bytes pairs; sizes expressed in non-byte units when constructing CacheDigest manually.
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
- staged blob does not match the declared CAS digest
- local CAS blob failed digest verification
- cannot publish an action result with a missing blob
- staged blob size does not match the declared CAS digest
- local action key already has a different result
AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22).
Data as JSON: /api/errors/928926baaa200370.
Report an issue: GitHub.
Appendix: source
Thrown at crates/mise-cache-core/src/local.rs:60
/// Find and verify a stored object.
pub fn find(&self, digest: &CacheDigest) -> Result<Option<PathBuf>> {
let path = self.path_for(digest)?;
if !path.exists() {
return Ok(None);
}
if !digest.matches_file(&path)? {
bail!(
"local CAS blob failed digest verification: {}",
path.display()
);
}
Ok(Some(path))
}
/// Atomically store bytes after verifying their declared digest.
pub fn store_bytes(&self, digest: &CacheDigest, bytes: &[u8]) -> Result<PathBuf> {
if !digest.matches_bytes(bytes)? {
bail!("bytes do not match the declared CAS digest");
}
self.store_with(digest, |temporary| {
temporary.write_all(bytes)?;
Ok(())
})
}
/// Atomically store a file after verifying its declared digest.
pub fn store_file(&self, digest: &CacheDigest, source: &Path) -> Result<PathBuf> {
self.store_file_inner(digest, source, true)
}
/// Store a file whose digest was already verified by this crate.
pub(crate) fn store_verified_file(
&self,
digest: &CacheDigest,
source: &Path,
) -> Result<PathBuf> {View on GitHub (pinned to 6f52dcdf99)