jdx/mise · error
brew-cask: structured flight glob '{}' matched outside stage
Error message
brew-cask: structured flight glob '{}' matched outside staged path What it means
Every path matched by a flight glob must stay under the staged cask directory. After expansion, each match is checked with `path.starts_with(staged_path)` and any escape aborts the install, enforcing the sandbox invariant that structured steps touch only staged files. In practice this fires when the staged root contains symlinked components or the pattern yields a lexically different root than the checker expects.
Source
Thrown at src/system/packages/brew/cask.rs:3943
let escaped_root = glob::Pattern::escape(staged_path.to_string_lossy().as_ref());
for pattern in expand_braces(pattern) {
validate_flight_relative_path(&pattern)?;
let rooted_pattern = Path::new(&escaped_root)
.join(Path::new(&pattern))
.to_string_lossy()
.to_string();
for path in glob::glob_with(
&rooted_pattern,
glob::MatchOptions {
require_literal_separator: true,
..Default::default()
},
)
.wrap_err_with(|| format!("brew-cask: invalid structured flight glob '{pattern}'"))?
{
let path = path?;
if !path.starts_with(staged_path) {
bail!(
"brew-cask: structured flight glob '{}' matched outside staged path",
pattern
);
}
matches.push(path);
}
}
matches.sort();
matches.dedup();
Ok(matches)
}
fn is_flight_glob(path: &str) -> bool {
path.chars()
.any(|c| matches!(c, '*' | '?' | '[' | ']' | '{' | '}'))
}
fn resolve_flight_path(staged_path: &Path, path: &FlightPath) -> Result<PathBuf> {View on GitHub (pinned to 6f52dcdf99)
Solutions
- Ensure the staged path used for globbing is the same real, non-symlinked root the matches are checked against (canonicalize the staged root)
- Remove `..` and absolute segments from glob patterns in the cask definition
- Re-stage the cask so staging metadata matches the on-disk location
Example fix
// before
let staged = "/tmp/caskroom/myapp"; // /tmp is a symlink on macOS
// after
let staged = std::fs::canonicalize("/tmp/caskroom/myapp")?; Defensive patterns
Strategy: validation
Validate before calling
// Canonicalize the staged root before any glob expansion so matches stay comparable
let staged_real = std::fs::canonicalize(staged_path)?;
for m in expand_staged_glob(&staged_real, pattern)? {
debug_assert!(m.starts_with(&staged_real), "glob escaped staged root: {}", m.display());
} Try / catch
if err.to_string().contains("matched outside staged path") {
// re-stage the cask; this indicates a symlinked/moved staging root, not a retryable error
} Prevention
- Avoid configuring the staging directory under symlinked paths like /tmp on macOS
- Reject glob patterns containing `..` at parse time
- Treat this error as a sandbox violation: fix the environment, never widen the check
When it happens
Trigger: The staged root is reached through a symlink the glob resolves differently than the prefix check expects (e.g. /tmp versus /private/tmp on macOS); patterns containing `..` or brace expansions producing rooted variants; a corrupted or adversarial cask stanza.
Common situations: A custom staging directory configured under a symlinked path; the staging directory moved between download and install; a hand-edited stanza with traversal segments.
Related errors
- brew-cask: invalid {kind} '{value}'
- brew-cask: staged symlink path escaped extraction root: {}
- brew-cask: refusing generic artifact source outside the extr
- brew-cask: refusing to stage generic artifact through a path
- brew-cask: structured move with multiple sources requires a
AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22).
Data as JSON: /api/errors/50acbc9e564c3b07.
Report an issue: GitHub.