jdx/mise · error

brew-cask: structured symlink globs must use staged_path

Error message

brew-cask: structured symlink globs must use staged_path

What it means

`flight_symlink_sources` only implements glob expansion against the staged cask directory. A copy or symlink step with `source_glob: true` whose source base is `AppDir`, `HomebrewPrefix` or `Literal` is rejected before any glob runs: only `FlightPathBase::StagedPath` is valid for globbed sources.

Source

Thrown at src/system/packages/brew/cask.rs:3854

        FlightGuard::IfExists(path) => {
            Ok(resolve_flight_path_with_context(cask, path, staged_path, appdir)?.exists())
        }
        FlightGuard::UnlessExists(path) => {
            Ok(!resolve_flight_path_with_context(cask, path, staged_path, appdir)?.exists())
        }
    }
}

fn flight_symlink_sources(
    cask: &Cask,
    source: &FlightPath,
    source_glob: bool,
    staged_path: &Path,
    appdir: &Path,
) -> Result<Vec<PathBuf>> {
    if source_glob {
        if source.base != FlightPathBase::StagedPath {
            bail!("brew-cask: structured symlink globs must use staged_path");
        }
        let pattern = expand_flight_template(cask, &source.path, staged_path, appdir);
        return expand_staged_glob(staged_path, &pattern);
    }
    Ok(vec![resolve_flight_path_with_context(
        cask,
        source,
        staged_path,
        appdir,
    )?])
}

fn create_flight_symlink(source: &Path, target: &Path, sudo: FlightSudo) -> Result<()> {
    match sudo {
        FlightSudo::Never => file::make_symlink(source, target).map(|_| ()),
        FlightSudo::IfNeeded => make_symlink_elevating(source, target),
        FlightSudo::Always => sudo::run("/bin/ln", &symlink_command_args(source, target), &[]),
    }

View on GitHub (pinned to 6f52dcdf99)

Solutions

  1. Drop the glob metacharacters and use a literal path with the non-staged base
  2. Keep the glob but express the source relative to the staged payload (StagedPath base)
  3. Restructure the artifact so only staged-payload files are ever globbed

Example fix

# before
symlink(source: "#{appdir}/*.app/Contents/MacOS/tool", source_glob: true, target: "#{prefix}/bin/tool")
# after - glob runs against the staged payload
symlink(source: "*.app/Contents/MacOS/tool", source_glob: true, target: "#{prefix}/bin/tool")
Defensive patterns

Strategy: type-guard

Type guard

// Narrow FlightPath values before combining a glob with a base
fn glob_source_is_supported(source: &FlightPath, source_glob: bool) -> bool {
    !source_glob || matches!(source.base, FlightPathBase::StagedPath)
}

Try / catch

if err.to_string().contains("symlink globs must use staged_path") {
    // strip the glob and use a literal path, or rebase the source onto the staged payload
}

Prevention

When it happens

Trigger: A cask stanza combining a non-staged base (an `$APPDIR`/`#{appdir}`-anchored source) with glob metacharacters; programmatic construction of a FlightPath with a non-staged base and `source_glob: true`.

Common situations: Hand-written or ported Homebrew ruby stanzas that glob absolute or appdir paths; templating that prefixes the appdir onto a wildcard pattern.

Related errors


AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22). Data as JSON: /api/errors/4d69792d3c3fc2fa. Report an issue: GitHub.