jdx/mise · error

brew-cask: invalid structured flight path '{}'

Error message

brew-cask: invalid structured flight path '{}'

What it means

A relative path used in a structured cask flight operation was rejected because it is absolute or contains a `..` (ParentDir) component. Homebrew validates every flight path to keep operations confined to the staged directory and throws this on any escape attempt or malformed path.

Source

Thrown at src/system/packages/brew/cask/flight.rs:1134

        .replace("{{staged_path}}", &staged_path.to_string_lossy())
        .replace("{{appdir}}", &appdir.to_string_lossy());
    if let Some(version) = version {
        value = value.replace("{{version}}", version);
    }
    if let Some(rest) = value.strip_prefix("~/") {
        value = crate::dirs::HOME.join(rest).to_string_lossy().to_string();
    }
    value
}

pub(super) fn validate_flight_relative_path(path: &str) -> Result<()> {
    let path = Path::new(path);
    if path.is_absolute()
        || path
            .components()
            .any(|component| matches!(component, Component::ParentDir))
    {
        bail!(
            "brew-cask: invalid structured flight path '{}'",
            path.display()
        );
    }
    Ok(())
}

pub(super) fn expand_braces(pattern: &str) -> Vec<String> {
    let Some(start) = pattern.find('{') else {
        return vec![pattern.to_string()];
    };
    let Some(end_offset) = pattern[start + 1..].find('}') else {
        return vec![pattern.to_string()];
    };
    let end = start + 1 + end_offset;
    let prefix = &pattern[..start];
    let suffix = &pattern[end + 1..];
    let mut expanded = Vec::new();

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Remove the leading `/` and make the path relative to staged_path
  2. Eliminate `..` components by restructuring the relative path
  3. Validate user- or upstream-supplied names before interpolating into flight paths
  4. Run `brew audit --strict` on the cask to catch bad paths pre-release

Example fix

// before
path: "/Applications/../../opt/App"
// after
path: "App.app"  // relative to staged_path
Defensive patterns

Strategy: validation

Validate before calling

fn is_safe_relative(p: &str) -> bool {
    let path = std::path::Path::new(p);
    !path.is_absolute()
        && !path.components().any(|c| c == std::path::Component::ParentDir)
}

Type guard

fn flight_path_ok(p: &str) -> bool {
    !std::path::Path::new(p).is_absolute()
        && std::path::Path::new(p).components().all(|c| c != std::path::Component::ParentDir)
}

Try / catch

match validate_flight_relative_path(p) {
    Err(e) => { log::warn!("bad flight path {p}: {e}"); sanitize_and_retry(p); }
    Ok(()) => proceed(),
}

Prevention

When it happens

Trigger: parse_flight_path, resolve_flight_path, or expand_staged_glob receive a path string that is absolute (`/Applications/App.app`) or includes `..` (e.g. `../../shared`); typically from a cask stanza or generated config.

Common situations: Cask authors accidentally writing absolute destinations; path templates that interpolate user/version strings producing `..`; copy-paste from non-staged-context scripts.

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/9229a7ced604c332. Report an issue: GitHub.