{"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":null,"httpStatus":null,"severity":"error","filePath":"crates/mise-cache-core/src/lib.rs","lineNumber":474,"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    fn capabilities_endpoint(&self) -> Result<Url> {\n        Ok(self\n            .base_url\n            .join(&format!(\"v{PROTOCOL_VERSION}/capabilities\"))?)\n    }\n\n    fn blob_pack_endpoint(&self) -> Result<Url> {\n        Ok(self\n            .base_url\n            .join(&format!(\"v{PROTOCOL_VERSION}/blobs:pack\"))?)\n    }","sourceCodeStart":456,"sourceCodeEnd":492,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/crates/mise-cache-core/src/lib.rs#L456-L492","documentation":"Thrown by action_manifest_endpoint (crates/mise-cache-core/src/lib.rs:474) when the key for a task action-manifest request validates but is not blake3. Manifest keys, like action keys, must be blake3 digests; the endpoint path v{PROTOCOL_VERSION}/action-manifests/{algorithm}/{hash}/{size} only accepts algorithm == \"blake3\".","triggerScenarios":"Calling get_action_manifest/put_action_manifest (and the ETag-verified GET path around lib.rs:710-726) with a key built from sha256 or another algorithm — e.g. deriving the manifest key by sha256 of the task name, or accidentally passing a blob digest as the manifest key.","commonSituations":"Code that hashes manifest keys with whatever hasher was in scope; version skew with a producer using a different key algorithm; confusion between the three digest roles (action key, manifest key, blob digest).","solutions":["Derive manifest keys with blake3 over the manifest's canonical bytes and label them \"blake3\"","Keep the digest roles distinct in your types or naming so a blob digest is never passed where a manifest key is expected","Align with the protocol: both action results and action manifests are blake3-keyed; only blob payloads may additionally be sha256"],"exampleFix":"// before\nlet key = CacheDigest { algorithm: \"sha256\".into(), hash: sha256_hex(&manifest_bytes), size };\n\n// after\nlet key = CacheDigest { algorithm: \"blake3\".into(), hash: blake3::hash(&manifest_bytes).to_hex().to_string(), size };","handlingStrategy":"type-guard","validationCode":"fn is_blake3_key(key: &CacheDigest) -> bool { key.algorithm == \"blake3\" }\n\nif !is_blake3_key(&manifest_key) { return Err(\"manifest keys must be blake3\"); }","typeGuard":"fn is_valid_manifest_key(k: &CacheDigest) -> bool {\n    k.algorithm == \"blake3\" && k.hash.len() == 64\n        && k.hash.bytes().all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))\n}","tryCatchPattern":"if !is_valid_manifest_key(&key) { rebuild_key_with_blake3(&manifest_bytes)?; }\nclient.put_action_manifest(&key, &bytes, expected_etag).await?;","preventionTips":["Derive manifest keys with blake3 over the exact serialized manifest bytes","Name variables manifest_key/blob_digest/action_key to keep the three roles from being swapped","Unit-test that every key you send passes key.validate() plus the blake3 check"],"tags":["mise","cache","validation","digest","blake3","action-manifest"],"backgroundTag":"unsupported-hash-algorithm","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}