{"record":{"id":"7b8b3404bd7064d4","repo":"jdx/mise","slug":"unsupported-remote-cache-digest-algorithm","errorCode":null,"errorMessage":"unsupported remote cache digest algorithm","messagePattern":"unsupported remote cache digest algorithm","errorType":"validation","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"crates/mise-cache-core/src/lib.rs","lineNumber":116,"sourceCode":"            algorithm: \"blake3\".into(),\n            hash: blake3::hash(bytes).to_hex().to_string(),\n            size: bytes.len() as u64,\n        }\n    }\n\n    /// Hash a file while counting the bytes read in the same streaming pass.\n    pub fn blake3_file(path: &Path) -> Result<Self> {\n        let (hash, size) = hash_file_blake3(path)?;\n        Ok(Self {\n            algorithm: \"blake3\".into(),\n            hash,\n            size,\n        })\n    }\n\n    pub fn validate(&self) -> Result<()> {\n        if self.algorithm != \"blake3\" && self.algorithm != \"sha256\" {\n            bail!(\"unsupported remote cache digest algorithm\");\n        }\n        if self.hash.len() != 64\n            || !self\n                .hash\n                .bytes()\n                .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))\n        {\n            bail!(\"invalid remote cache digest\");\n        }\n        Ok(())\n    }\n\n    pub fn matches_bytes(&self, bytes: &[u8]) -> Result<bool> {\n        self.validate()?;\n        if self.size != bytes.len() as u64 {\n            return Ok(false);\n        }\n        let hash = match self.algorithm.as_str() {","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/crates/mise-cache-core/src/lib.rs#L98-L134","documentation":"CacheDigest::validate() only accepts the exact, case-sensitive algorithm strings \"blake3\" and \"sha256\". This error fires when any digest handed to the remote cache or local CAS (blobs, action results, manifests) declares a different algorithm name. It is the first guard in validate(), so it triggers before the hash-format check and a well-formed hash never masks a bad algorithm.","triggerScenarios":"Manually constructing a CacheDigest with algorithm set to \"SHA256\", \"Blake3\", \"blake2b\", \"\" (or a typo) and passing it to RemoteCacheClient::get_blob/get_action_result/get_action_manifest, LocalCas::path_for/find/store_bytes/store_file, LocalActionCache::find/store, or calling digest.matches_bytes()/matches_file(). Also thrown when a CacheDigest deserialized from JSON (RemoteActionResult, CacheDirectory nodes) carries a renamed or re-cased algorithm field.","commonSituations":"Interoperating with manifests or servers written by a different tool version that uses different algorithm names; a JSON processing layer that uppercases strings; hand-built digests in test fixtures; digests copied from a sha1/sha512-based system.","solutions":["Set algorithm to the exact lowercase string \"blake3\" or \"sha256\" — the comparison is case-sensitive","Prefer the constructors CacheDigest::blake3(bytes) or CacheDigest::blake3_file(path), which always set the algorithm correctly","If the digest comes from serialized data, normalize the algorithm to lowercase and confirm it is one of the two supported names before use","Call digest.validate() as soon as a digest arrives from an external source so the failure happens at the boundary with a clear error"],"exampleFix":"// before\nlet digest = CacheDigest { algorithm: \"SHA256\".into(), hash, size };\nclient.get_blob(&digest, media).await?;\n\n// after\nlet digest = CacheDigest { algorithm: \"sha256\".into(), hash: hash.to_lowercase(), size };\nclient.get_blob(&digest, media).await?;\n\n// best: never assemble by hand\nlet digest = CacheDigest::blake3(&bytes);","handlingStrategy":"validation","validationCode":"fn ensure_supported_digest(digest: &CacheDigest) -> eyre::Result<()> {\n    if !matches!(digest.algorithm.as_str(), \"blake3\" | \"sha256\") {\n        eyre::bail!(\"unsupported digest algorithm {:?}\", digest.algorithm);\n    }\n    digest.validate()\n}\n// run before any RemoteCacheClient / LocalCas / LocalActionCache call","typeGuard":"fn has_supported_algorithm(digest: &CacheDigest) -> bool {\n    matches!(digest.algorithm.as_str(), \"blake3\" | \"sha256\")\n}","tryCatchPattern":null,"preventionTips":["Construct digests with CacheDigest::blake3()/blake3_file() instead of struct literals","Validate digests immediately after deserializing external JSON","Keep algorithm strings lowercase at every serialization boundary"],"tags":["digest","validation","remote-cache","cas","rust"],"backgroundTag":"digest-algorithm-mismatch","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}