{"record":{"id":"52136fa4e88eac4c","repo":"jdx/mise","slug":"invalid-remote-task-cache-key","errorCode":null,"errorMessage":"invalid remote task cache key","messagePattern":"invalid remote task cache key","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/task/task_cache_store.rs","lineNumber":840,"sourceCode":"            }\n            RestoredNode::Symlink { target, .. } => {\n                header.set_size(0);\n                archive.append_link(&mut header, path, target)?;\n            }\n        }\n    }\n    let encoder = archive.into_inner()?;\n    encoder.finish()?;\n    Ok(archive_file)\n}\n\nfn validate_remote_key(key: &str) -> Result<()> {\n    if key.len() != 64\n        || !key\n            .bytes()\n            .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))\n    {\n        bail!(\"invalid remote task cache key\");\n    }\n    Ok(())\n}\n\npub(crate) struct LocalTaskCacheStore {\n    root: PathBuf,\n}\n\nimpl LocalTaskCacheStore {\n    pub(crate) fn new(root: PathBuf) -> Self {\n        Self { root }\n    }\n\n    fn paths(&self, key: &str) -> (PathBuf, PathBuf) {\n        (\n            self.root.join(format!(\"{key}.tar.zst\")),\n            self.root.join(format!(\"{key}.json\")),\n        )","sourceCodeStart":822,"sourceCodeEnd":858,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/task/task_cache_store.rs#L822-L858","documentation":"Remote task cache keys must be exactly 64 lowercase hex characters (a sha-256 digest). validate_remote_key enforces length 64 and restricts bytes to 0-9 and a-f. Any other string — uppercase hex, truncated hashes, path-like or free-form keys — is rejected before it can be used as a remote cache key.","triggerScenarios":"A remote cache store operation receives a key that is not 64-char lowercase hex: an uppercase digest, a truncated/prefixed hash, a human-readable key, or a key containing '/', ':', or whitespace.","commonSituations":"Hand-rolled cache keys instead of hashing inputs; a hex encoder emitting uppercase; truncating digests; a custom backend that uses non-digest keys.","solutions":["Hash the cache inputs with sha-256 and hex-encode lowercase before passing the key","Lowercase any uppercase hex digests: key.to_ascii_lowercase()","Use the full 64-character digest — do not truncate or prefix it","If using a custom backend, ensure it derives 64-char lowercase hex keys"],"exampleFix":"// before\nlet key = format!(\"cache-{}\", input_hash_hex.to_uppercase());\n// after\nuse sha2::{Digest, Sha256};\nlet mut h = Sha256::new();\nh.update(cache_inputs);\nlet key = hex::encode(h.finalize()); // 64 lowercase hex chars","handlingStrategy":"validation","validationCode":"fn is_valid_remote_key(key: &str) -> bool {\n    key.len() == 64\n        && key.bytes().all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))\n}","typeGuard":null,"tryCatchPattern":"let key = compute_cache_key(inputs);\nif !is_valid_remote_key(&key) {\n    return Err(anyhow!(\"cache key must be 64-char lowercase hex, got {key:?}\"));\n}\nstore.get(&key).await?","preventionTips":["Always derive remote cache keys via sha-256 + lowercase hex encoding","Add a key-format unit test in any custom uploader","Never pass user-supplied strings directly as cache keys","Use hex::encode (lowercase) rather than format! with {:X}"],"tags":["cache","validation","hash","hex"],"backgroundTag":"invalid-identifier-format","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}