jdx/mise · error

remote cache path must be relative

Error message

remote cache path must be relative

What it means

validate_cache_path rejects cache paths that are empty or absolute. Remote cache entry paths (manifest roots, archive entries, tree nodes) must be relative so they can be safely joined under a local output directory. Absolute paths would escape the artifact directory when materialized.

Solutions

  1. Regenerate the cache archive/manifest so all paths are relative to the output root (strip leading '/' and mount prefixes).
  2. Re-run the task to republish a clean entry rather than reusing the offending archive.
  3. If building archives yourself, use relative paths: tar -C <outdir> . instead of tar <abs-path>.
  4. Reject/inspect the remote entry at ingestion before it enters the cache.

Example fix

// before (creating archive)
$ tar -czf out.tar.zst /home/me/project/dist
// after
$ cd /home/me/project && tar -czf out.tar.zst dist
Defensive patterns

Strategy: validation

Validate before calling

fn is_safe_relative(p: &Path) -> bool {
    !p.as_os_str().is_empty() && !p.is_absolute()
}

Type guard

fn is_valid_cache_path(p: &std::path::Path) -> bool {
    use std::path::Component::*;
    !p.as_os_str().is_empty()
        && !p.is_absolute()
        && p.components().all(|c| matches!(c, Normal(_)))
}

Try / catch

match validate_cache_path(&entry_path) {
    Ok(()) => {},
    Err(e) if e.to_string().contains("must be relative") => {
        return Err(eyre!("cache archive {key} has absolute path {entry_path:?}; rebuild it"));
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: get() validating manifest roots, archive_to_cas() validating tar entry paths, or materialize_remote_tree() validating node paths when a path is empty or starts with '/' (or a Windows prefix) (task_cache_store.rs:503).

Common situations: A hand-crafted or third-party-written cache archive containing absolute paths (e.g. tar entries like /home/user/out); manifests edited manually; archives created with different tar tooling including absolute members.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/0b349b84eed475c4. Report an issue: GitHub.

Appendix: source

Thrown at src/task/task_cache_store.rs:503

                action: action_digest,
                metadata: Some(metadata),
                output_root,
                version: 1,
            })
            .await
    }

    async fn remove(&self, _key: &str) -> Result<()> {
        // Ordinary cache writers intentionally have no remote-delete authority.
        Ok(())
    }

    fn touch(&self, _key: &str) {}
}

fn validate_cache_path(path: &Path) -> Result<()> {
    if path.as_os_str().is_empty() || path.is_absolute() {
        bail!("remote cache path must be relative");
    }
    if path.components().any(|component| {
        matches!(
            component,
            Component::ParentDir | Component::RootDir | Component::Prefix(_)
        )
    }) {
        bail!("remote cache path escapes its output root");
    }
    Ok(())
}

fn cache_name(path: &Path) -> Result<String> {
    let name = path
        .file_name()
        .and_then(|name| name.to_str())
        .ok_or_else(|| eyre!("remote cache paths must be valid UTF-8"))?;
    if name.is_empty() || name == "." || name == ".." || name.contains(['/', '\0']) {

View on GitHub (pinned to 533346cc37)