{"record":{"id":"0b349b84eed475c4","repo":"jdx/mise","slug":"remote-cache-path-must-be-relative","errorCode":null,"errorMessage":"remote cache path must be relative","messagePattern":"remote cache path must be relative","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/task/task_cache_store.rs","lineNumber":503,"sourceCode":"                action: action_digest,\n                metadata: Some(metadata),\n                output_root,\n                version: 1,\n            })\n            .await\n    }\n\n    async fn remove(&self, _key: &str) -> Result<()> {\n        // Ordinary cache writers intentionally have no remote-delete authority.\n        Ok(())\n    }\n\n    fn touch(&self, _key: &str) {}\n}\n\nfn validate_cache_path(path: &Path) -> Result<()> {\n    if path.as_os_str().is_empty() || path.is_absolute() {\n        bail!(\"remote cache path must be relative\");\n    }\n    if path.components().any(|component| {\n        matches!(\n            component,\n            Component::ParentDir | Component::RootDir | Component::Prefix(_)\n        )\n    }) {\n        bail!(\"remote cache path escapes its output root\");\n    }\n    Ok(())\n}\n\nfn cache_name(path: &Path) -> Result<String> {\n    let name = path\n        .file_name()\n        .and_then(|name| name.to_str())\n        .ok_or_else(|| eyre!(\"remote cache paths must be valid UTF-8\"))?;\n    if name.is_empty() || name == \".\" || name == \"..\" || name.contains(['/', '\\0']) {","sourceCodeStart":485,"sourceCodeEnd":521,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/task/task_cache_store.rs#L485-L521","documentation":"mise's remote task cache rejects cache entry paths that are empty or absolute before storing or materializing them. Remote cache paths are always interpreted relative to a per-task output root, so an absolute path would break the digest-addressed store and could write outside it. validate_cache_path runs before get, archive_to_cas, and materialize_remote_tree.","triggerScenarios":"Calling get, archive_to_cas (via commit), or materialize_remote_tree with a cache path that is empty (as_os_str().is_empty()) or absolute (Path::is_absolute, e.g. \"/abs/out.bin\" on Unix or \"C:\\\\x\" on Windows).","commonSituations":"Task [out] declarations or task configs that accidentally use an absolute path for a cached output; code that joins a root directory into the path before passing it to the cache API instead of passing the root-relative path.","solutions":["Make the path relative to the task's output root before passing it to the cache API.","Check for a task definition where a cached output is specified as an absolute path and change it to a relative path.","If you have an absolute path programmatically, strip the output root prefix (path.strip_prefix(root)?) before caching."],"exampleFix":"// before\nstore.get(Path::new(\"/abs/build/out.bin\"), &key).await?;\n// after\nlet rel = Path::new(\"/abs/build/out.bin\").strip_prefix(\"/abs/build\")?;\nstore.get(rel, &key).await?;","handlingStrategy":"validation","validationCode":"fn is_valid_cache_path(p: &Path) -> bool {\n    !p.as_os_str().is_empty() && !p.is_absolute()\n}\n// call: assert!(is_valid_cache_path(&rel_path), \"path must be relative\");","typeGuard":"fn as_relative_cache_path(p: &Path) -> Option<&Path> {\n    (!p.as_os_str().is_empty() && !p.is_absolute()).then_some(p)\n}","tryCatchPattern":"match store.get(&rel, &key).await {\n    Err(e) if e.to_string().contains(\"remote cache path must be relative\") => {\n        // fix path: strip the root prefix and retry\n    }\n    r => r?,\n}","preventionTips":["Always pass paths relative to the task output root into the cache API","Use Path::strip_prefix(root) instead of manual string slicing","Add a debug assertion that cached paths are relative"],"tags":["path-validation","task-cache","remote-cache"],"backgroundTag":"invalid-argument-value","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}