jdx/mise · error
local CAS blob failed digest verification
Error message
local CAS blob failed digest verification: {} What it means
`LocalCas::find` located the file at its content-addressed path (`cas/v1/<algo>/<hash[0..2]>/<hash>-<size>`) but re-hashing the file no longer matches the digest encoded in the path. The CAS treats this as corruption: the blob is not returned and the error names the offending path. Because paths derive from digests, something must have mutated the file after it was stored.
Solutions
- Delete the offending blob named in the error (or the whole `cas/v1` subtree) - the next run recomputes and re-stores it.
- Run disk diagnostics (SMART, fsck) if corruption recurs.
- Ensure nothing outside mise writes into the cache root, and exclude it from antivirus/backup tools that rewrite files.
- In calling code, treat `find` errors as cache misses: recompute and `store_bytes` to heal the entry.
Example fix
// before: propagate the corruption error
let path = cas.find(&digest)?.unwrap();
// after: treat a corrupt blob as a miss and heal it
let path = match cas.find(&digest) {
Ok(Some(path)) => path,
_ => cas.store_bytes(&digest, &recompute())?,
}; Defensive patterns
Strategy: fallback
Try / catch
let path = match cas.find(&digest) {
Ok(Some(path)) => path,
Ok(None) => cas.store_bytes(&digest, &produce())?,
Err(err) => {
warn!("corrupt CAS blob, healing: {err}");
cas.store_bytes(&digest, &produce())? // recompute and overwrite by re-storing
}
}; Prevention
- Treat CAS find errors as cache misses and recompute; never propagate corruption to users.
- Keep the cache root owned by one tool; exclude it from editors, cleaners, and antivirus.
- Monitor for repeat corruption on the same path - that indicates failing hardware.
When it happens
Trigger: Any `find`/`store_file` call that hits a CAS entry whose on-disk contents were changed: another process (editor, antivirus, backup or cleanup tool, user) modified or truncated files under the cache root; bit rot or filesystem corruption; a foreign file placed at the digest path by a different cache layout or version.
Common situations: Hand-editing or sparse-truncating files under the cache root; failing disks; sharing one cache directory between incompatible tool versions; cleanup utilities that touch files they should not.
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
- bytes do not match the declared CAS digest
- staged blob does not match the declared CAS digest
- 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/493b50a67eababe2.
Report an issue: GitHub.
Appendix: source
Thrown at crates/mise-cache-core/src/local.rs:49
/// Resolve the storage path for a validated digest.
pub fn path_for(&self, digest: &CacheDigest) -> Result<PathBuf> {
digest.validate()?;
Ok(self
.root
.join("cas/v1")
.join(&digest.algorithm)
.join(&digest.hash[..2])
.join(format!("{}-{}", digest.hash, digest.size)))
}
/// 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(())
})
}
View on GitHub (pinned to 6f52dcdf99)