jdx/mise · error

remote cache path escapes its output root

Error message

remote cache path escapes its output root

What it means

Thrown by validate_cache_path when mise restores a task output from the HTTP remote cache. Every path that comes back from the remote store (the manifest's roots array and each joined child path in the directory tree) must be a plain relative path: absolute paths and any ParentDir, RootDir, or Windows drive-Prefix component are rejected. It is a path-traversal guard so a corrupt or hostile cache server cannot make mise write outside the restore directory.

Source

Thrown at src/task/task_cache_store.rs:511

    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']) {
        bail!("invalid remote cache path component");
    }
    Ok(name.to_string())
}

fn validate_cache_name(name: &str) -> Result<()> {
    let path = Path::new(name);
    if name.is_empty()

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Purge the affected entry (action result + blobs) from the remote cache and re-run the task so a trusted mise re-uploads it
  2. Verify every writer to the shared cache runs the same pinned mise version (CI included)
  3. If you operate the cache server, validate directory protos and manifests for relative, single-tree paths at upload time
  4. Reproduce with MISE_DEBUG=1 to capture the offending digest and inspect the blob it served

Example fix

// remote manifest roots (downloaded blob)
// before
"roots": ["/home/ci/build/out"]
// after
"roots": ["out"]
Defensive patterns

Strategy: validation

Validate before calling

use std::path::{Component, Path};

fn is_safe_cache_path(path: &Path) -> bool {
    !path.as_os_str().is_empty()
        && !path.is_absolute()
        && !path.components().any(|c| matches!(
            c,
            Component::ParentDir | Component::RootDir | Component::Prefix(_)
        ))
}

// before trusting a downloaded manifest:
for root in &manifest.roots {
    assert!(is_safe_cache_path(Path::new(root)), "corrupt cache entry");
}

Type guard

fn is_safe_cache_path(path: &std::path::Path) -> bool {
    !path.as_os_str().is_empty()
        && !path.is_absolute()
        && !path.components().any(|c| matches!(
            c,
            std::path::Component::ParentDir
                | std::path::Component::RootDir
                | std::path::Component::Prefix(_)
        ))
}

Try / catch

match restore_result {
    Ok(entry) => entry,
    Err(err) if err.to_string().contains("escapes its output root") => {
        warn!("corrupt remote cache entry; purging and rebuilding");
        purge_remote_entry(&key).await?;
        run_uncached(&task).await?
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: HttpTaskCacheStore::get() downloads a manifest whose roots contain e.g. "/out" or "a/../../etc"; or materialize_remote_tree() joins a remote directory entry name with its parent and the result contains a ParentDir/RootDir/Prefix component. The bail happens before any file is written.

Common situations: Shared CAS/HTTP cache written by non-mise tooling or hand-edited manifests; a proxy or script rewriting JSON bodies; version skew where another mise release serialized roots differently; deliberate tampering tests against the cache server.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/6b8fef3ea2823426. Report an issue: GitHub.