{"record":{"id":"a85c0fd79bff84ca","repo":"jdx/mise","slug":"remote-action-manifest-keys-must-use-blake3","errorCode":null,"errorMessage":"remote action manifest keys must use blake3","messagePattern":"remote action manifest keys must use blake3","errorType":"validation","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"crates/mise-cache-core/src/lib.rs","lineNumber":326,"sourceCode":"        }\n        Ok(self.base_url.join(&format!(\n            \"v{PROTOCOL_VERSION}/action-results/{}/{}/{}\",\n            action.algorithm, action.hash, action.size\n        ))?)\n    }\n\n    fn blob_endpoint(&self, digest: &CacheDigest) -> Result<Url> {\n        digest.validate()?;\n        Ok(self.base_url.join(&format!(\n            \"v{PROTOCOL_VERSION}/blobs/{}/{}/{}\",\n            digest.algorithm, digest.hash, digest.size\n        ))?)\n    }\n\n    fn action_manifest_endpoint(&self, key: &CacheDigest) -> Result<Url> {\n        key.validate()?;\n        if key.algorithm != \"blake3\" {\n            bail!(\"remote action manifest keys must use blake3\");\n        }\n        Ok(self.base_url.join(&format!(\n            \"v{PROTOCOL_VERSION}/action-manifests/{}/{}/{}\",\n            key.algorithm, key.hash, key.size\n        ))?)\n    }\n\n    async fn request(\n        &self,\n        method: reqwest::Method,\n        url: Url,\n        media_type: &'static str,\n    ) -> Result<reqwest::RequestBuilder> {\n        let request = self\n            .client\n            .request(method, url)\n            .header(PROTOCOL_HEADER, u16::from(PROTOCOL_VERSION))\n            .header(NAMESPACE_HEADER, &self.namespace)","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/crates/mise-cache-core/src/lib.rs#L308-L344","documentation":"Action-manifest keys are scoped like action-result keys: blake3 only. action_manifest_endpoint() validates the digest and then rejects any algorithm other than \"blake3\" before constructing the URL for get_action_manifest/put_action_manifest. This keeps manifest keys consistent with the blake3 ETag contract used for manifest bodies.","triggerScenarios":"Passing a sha256 CacheDigest as the key to RemoteCacheClient::get_action_manifest or put_action_manifest; deriving the manifest key from a sha256 blob digest; shared helper code that hashes keys with a configurable algorithm defaulting to sha256.","commonSituations":"Code paths that compute blob digests (any algorithm) and manifest keys (blake3 only) with the same function; integrations upgraded from an earlier prototype that used sha256 manifest keys.","solutions":["Derive manifest keys with CacheDigest::blake3 over the exact manifest bytes","Keep one code path for key digests (blake3) and a separate one for blob digests if you need sha256 blobs","Add a debug assert or pre-check that key.algorithm == \"blake3\" before calling the manifest endpoints"],"exampleFix":"// before\nlet key = CacheDigest { algorithm: \"sha256\".into(), hash, size };\nclient.get_action_manifest(&key).await?;\n\n// after\nlet key = CacheDigest::blake3(&manifest_bytes);\nclient.get_action_manifest(&key).await?;","handlingStrategy":"validation","validationCode":"fn manifest_key(bytes: &[u8]) -> CacheDigest {\n    CacheDigest::blake3(bytes) // manifest keys are always blake3\n}\n\nfn ensure_manifest_key(key: &CacheDigest) -> eyre::Result<()> {\n    key.validate()?;\n    if key.algorithm != \"blake3\" {\n        eyre::bail!(\"manifest key must use blake3, got {}\", key.algorithm);\n    }\n    Ok(())\n}","typeGuard":"fn is_blake3_manifest_key(key: &CacheDigest) -> bool {\n    key.algorithm == \"blake3\" && key.validate().is_ok()\n}","tryCatchPattern":null,"preventionTips":["Derive manifest keys from the exact manifest bytes with CacheDigest::blake3","Never route manifest keys through a configurable-algorithm hashing helper","Type-separate key digests from blob digests in your data model so they cannot be swapped"],"tags":["digest","blake3","manifest","remote-cache"],"backgroundTag":"digest-algorithm-mismatch","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}