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
- Remove the leading `/` and make the path relative to staged_path
- Eliminate `..` components by restructuring the relative path
- Validate user- or upstream-supplied names before interpolating into flight paths
- 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
- Sanitize interpolated names (versions, tokens) before joining paths
- Write stanzas with relative paths only
- Run `brew audit --strict` to catch traversal-prone paths
- Never accept user-supplied absolute destinations in generated flight config
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
- brew-cask:{}: invalid {kind} {field} path {}
- brew-cask: structured flight glob '{}' matched outside stage
- brew-cask: binary target '{}' must be under {}
- brew-cask: refusing generic artifact copy outside Homebrew p
- invalid recovery destination
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/9229a7ced604c332.
Report an issue: GitHub.