jdx/mise · error
brew-cask: structured symlink source '{}' did not match any
Error message
brew-cask: structured symlink source '{}' did not match any paths What it means
A structured `symlink` step found no source paths. Sources resolve through `flight_symlink_sources`: with `source_glob: true` the template-expanded pattern is globbed under the staged path, and an empty result is fatal because a link cannot be created from nothing. All `guards` must have matched for execution to reach this check.
Source
Thrown at src/system/packages/brew/cask.rs:3627
force,
uninstall,
source_glob,
sudo,
guards,
} => {
if !guards
.iter()
.map(|guard| flight_guard_matches(cask, guard, staged_path, appdir))
.collect::<Result<Vec<_>>>()?
.into_iter()
.all(|matches| matches)
{
return Ok(());
}
let target = resolve_flight_path_with_context(cask, target, staged_path, appdir)?;
let sources = flight_symlink_sources(cask, source, *source_glob, staged_path, appdir)?;
if sources.is_empty() {
bail!(
"brew-cask: structured symlink source '{}' did not match any paths",
source.path
);
}
let target_metadata = target.symlink_metadata().ok();
let target_is_real_dir = target_metadata
.as_ref()
.is_some_and(|metadata| metadata.is_dir());
let target_is_dir = target_is_real_dir || sources.len() > 1;
if sources.len() > 1 {
let created_external_directory =
target_metadata.is_none() && !target.starts_with(staged_path);
if target_metadata.is_some() && !target_is_real_dir {
if target.exists() && !force && !targets.previous_symlinks.contains(&target) {
bail!(
"brew-cask: structured symlink target '{}' already exists",
target.display()
);View on GitHub (pinned to 6f52dcdf99)
Solutions
- List the staged directory and correct the source pattern to the actual file name
- Template the version-dependent segments so the pattern tracks releases
- Guard optional symlinks with `if_exists` so absent sources skip instead of fail
- Re-stage the payload if extraction may have been partial
Example fix
# before
symlink(source: "mytool-1.*/bin/mytool", source_glob: true, target: "#{prefix}/bin/mytool")
# after
symlink(source: "mytool-#{version}/bin/mytool", source_glob: true, target: "#{prefix}/bin/mytool") Defensive patterns
Strategy: validation
Validate before calling
// Pre-check symlink glob sources match at least one path
let sources = flight_symlink_sources(cask, &step.source, step.source_glob, staged_path, appdir)?;
if sources.is_empty() {
return Err(eyre!("symlink source '{}' matched nothing under staged payload", step.source.path));
} Try / catch
if err.to_string().contains("symlink source") && err.to_string().contains("did not match any paths") {
// dump the staged directory tree to pinpoint the renamed file
} Prevention
- Template version-stamped names in symlink sources
- Guard optional binaries with `if_exists` so absent sources skip cleanly
- Re-test cask stanzas against the new payload before publishing a version bump
When it happens
Trigger: `symlink` with `source_glob: true` matching zero files in the staged payload; a version-stamped source template expanding to a name that no longer exists; the binary was renamed upstream.
Common situations: Cask version bump changing binary names; archive layout change between releases; typo or wrong letter-case in the glob.
Related errors
- brew-cask: structured move with multiple sources requires a
- brew-cask: structured copy source must resolve to exactly on
- brew-cask: structured symlink target '{}' already exists
- brew-cask: structured symlink globs must use staged_path
- brew-cask: structured symlink source '{}' did not match any
AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22).
Data as JSON: /api/errors/4a06a0aff06433ea.
Report an issue: GitHub.