jdx/mise · error · eyre::Report
invalid remote cache digest
Error message
invalid remote cache digest
What it means
validate() requires the hash field to be exactly 64 characters of lowercase hexadecimal (0-9, a-f). Both blake3's standard 32-byte output and sha256 produce exactly this form, so the error indicates uppercase hex, a wrong-length digest, or non-hex characters. The check runs on every operation because the hash is interpolated directly into remote URLs and filesystem paths.
Source
Thrown at crates/mise-cache-core/src/lib.rs:124
let (hash, size) = hash_file_blake3(path)?;
Ok(Self {
algorithm: "blake3".into(),
hash,
size,
})
}
pub fn validate(&self) -> Result<()> {
if self.algorithm != "blake3" && self.algorithm != "sha256" {
bail!("unsupported remote cache digest algorithm");
}
if self.hash.len() != 64
|| !self
.hash
.bytes()
.all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
{
bail!("invalid remote cache digest");
}
Ok(())
}
pub fn matches_bytes(&self, bytes: &[u8]) -> Result<bool> {
self.validate()?;
if self.size != bytes.len() as u64 {
return Ok(false);
}
let hash = match self.algorithm.as_str() {
"blake3" => blake3::hash(bytes).to_hex().to_string(),
"sha256" => hex::encode(sha2::Sha256::digest(bytes)),
_ => unreachable!("digest algorithm was validated"),
};
Ok(self.hash == hash)
}
pub fn matches_file(&self, path: &Path) -> Result<bool> {View on GitHub (pinned to 9dcfcaa0dc)
Solutions
- Lowercase the hash and strip any whitespace, quotes, or 0x prefix before constructing the digest
- Verify you used the standard 32-byte digest: blake3::Hasher::finalize() (not extended output) or Sha256::digest
- Recompute the digest from the actual content with CacheDigest::blake3()/blake3_file() instead of assembling it by hand
- Run digest.validate() immediately after deserializing digests from external JSON to catch format drift early
Example fix
// before
let digest = CacheDigest { algorithm: "blake3".into(), hash: hash.to_uppercase(), size };
// after
let digest = CacheDigest { algorithm: "blake3".into(), hash: hash.to_lowercase(), size }; Defensive patterns
Strategy: validation
Validate before calling
fn has_valid_hash_format(digest: &CacheDigest) -> bool {
digest.hash.len() == 64
&& digest.hash.bytes().all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
}
fn normalized_digest(digest: &CacheDigest) -> eyre::Result<CacheDigest> {
let mut d = digest.clone();
d.algorithm = d.algorithm.to_lowercase();
d.hash = d.hash.trim().trim_start_matches("0x").to_lowercase();
d.validate()?;
Ok(d)
} Type guard
fn is_well_formed_digest(digest: &CacheDigest) -> bool {
matches!(digest.algorithm.as_str(), "blake3" | "sha256")
&& digest.hash.len() == 64
&& digest.hash.bytes().all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
} Prevention
- Use standard 32-byte digests (blake3 finalize, sha256) — never extended blake3 output
- Lowercase hashes at ingestion from external systems
- Call digest.validate() at trust boundaries instead of deep inside store logic
When it happens
Trigger: A digest whose hash is uppercase hex; a 40-character sha1 hash; a 128-character blake3 extended (XOF) output; a hash with a "0x" prefix, whitespace, or newline; an empty string. Thrown from validate() reached via matches_bytes, matches_file, all RemoteCacheClient endpoint builders, and LocalCas/LocalActionCache path_for/find/store.
Common situations: Copying hashes from tools that render hex uppercase; using blake3 extend_output instead of finalize; hand-editing manifest JSON; test fixtures with placeholder hashes like "deadbeef"; a database column or template that trims or re-encodes the hash.
Related errors
- unsupported remote cache digest algorithm
- remote cache action keys must use blake3
- remote action manifest keys must use blake3
- invalid remote action manifest ETag
- remote cache URL must use HTTPS
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/bb096f77fc103092.
Report an issue: GitHub.