jdx/mise · error

relative remote mise path escapes the staged project: {comma

Error message

relative remote mise path escapes the staged project: {command:?}

What it means

For relative remote mise paths, mise normalizes components and rejects any path that would climb above the staged project root: when a ".." component has no matching parent component left, the path escapes the project and mise bails instead of executing outside it.

Source

Thrown at src/system/remote.rs:1252

) -> Result<String> {
    if command.starts_with('/') {
        return Ok(command.to_string());
    }
    if let Some(suffix) = command.strip_prefix("~/") {
        if suffix.is_empty() {
            bail!("remote mise path does not name an executable: {command:?}");
        }
        let home = remote_home.ok_or_else(|| eyre!("remote login home was not resolved"))?;
        return Ok(format!("{}/{suffix}", home.trim_end_matches('/')));
    }

    let mut components = Vec::new();
    for component in command.split('/') {
        match component {
            "" | "." => {}
            ".." => {
                if components.pop().is_none() {
                    bail!("relative remote mise path escapes the staged project: {command:?}");
                }
            }
            component => components.push(component),
        }
    }
    if components.is_empty() {
        bail!("relative remote mise path does not name an executable: {command:?}");
    }
    Ok(format!("{project}/{}", components.join("/")))
}

fn remote_mise_find_script() -> &'static str {
    r#"mise_path=$(command -v mise 2>/dev/null || true)
case "$mise_path" in /*) ;; *) mise_path= ;; esac
if [ -z "$mise_path" ]; then
  for candidate in "$HOME/.local/bin/mise" "$HOME/.local/share/mise/bin/mise" "$HOME/.cargo/bin/mise" /usr/local/bin/mise /opt/homebrew/bin/mise; do
    if [ -x "$candidate" ]; then
      mise_path=$candidate

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Use a path that stays within the staged project, e.g. "bin/mise" or "./mise"
  2. Use an absolute path (starting with /) or home-relative "~/..." if the binary lives elsewhere
  3. Install mise at the default location so no relative path is needed

Example fix

// before
remote_mise = "../tools/mise"
// after
remote_mise = "~/tools/mise"
Defensive patterns

Strategy: validation

Validate before calling

function staysInProject(rel) {
  let depth = 0;
  for (const seg of rel.split('/')) {
    if (seg === '..') { depth--; if (depth < 0) return false; }
    else if (seg && seg !== '.') depth++;
  }
  return true;
}

Type guard

const inProject = (rel) => !path.posix.isAbsolute(rel) && !rel.split('/').reduce((d,s)=> s==='..'? d-1 : (s&&s!=='.')? d+1 : d, 0) < 0 === false;

Prevention

When it happens

Trigger: Supplying a relative remote mise command like "../tools/mise" or "a/../../mise" whose normalization would resolve outside the staged project directory.

Common situations: Users assuming relative paths are relative to CWD or home rather than the staged project; paths copied from layouts where mise sat in a sibling directory.

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@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/71bfd941be12ee6a. Report an issue: GitHub.