jdx/mise · error
brew-cask: structured copy source must resolve to exactly on
Error message
brew-cask: structured copy source must resolve to exactly one path
What it means
During a brew-cask structured copy flight step, the source selector is expected to resolve to exactly one path. When `source_glob` is enabled, the glob is expanded and this error fires if it matches zero or more than one path, because a copy step copies a single artifact onto a single target path. This is a cask-definition sanity check to avoid ambiguous copies.
Source
Thrown at src/system/packages/brew/cask/flight.rs:651
file::remove_file_or_dir(&path)?;
}
}
}
}
FlightStep::Copy {
source,
target,
recursive,
overwrite,
source_glob,
guards,
} => {
if !flight_guards_pass(cask, guards, staged_path, appdir)? {
return Ok(());
}
let sources = flight_symlink_sources(cask, source, *source_glob, staged_path, appdir)?;
let [source] = sources.as_slice() else {
bail!("brew-cask: structured copy source must resolve to exactly one path");
};
if !source.exists() {
bail!(
"brew-cask: structured copy source '{}' was not found",
source.display()
);
}
if source.is_dir() && !recursive {
bail!("brew-cask: structured directory copy requires recursive=true");
}
let target = resolve_flight_path_with_context(cask, target, staged_path, appdir)?;
let external = !target.starts_with(staged_path);
let target_metadata = target.symlink_metadata().ok();
if target_metadata.is_some() {
if !overwrite {
bail!(
"brew-cask: structured copy target '{}' already exists",
target.display()View on GitHub (pinned to afd2eddd3a)
Solutions
- Tighten the copy source glob so it matches exactly one path (add distinguishing segments, e.g. " MyApp.app" instead of "*.app").
- If multiple matches are intended, replace the single Copy step with a Symlink step (which supports directory targets and multiple sources) or several Copy steps.
- If the glob matched nothing, verify the file exists in the staged path at this install step (it may only appear after a prior Move/Copy step).
Example fix
// before
FlightStep::Copy { source: glob("**/*.app"), source_glob: true, .. }
// after
FlightStep::Copy { source: path("MyApp.app"), source_glob: false, .. } Defensive patterns
Strategy: validation
Validate before calling
let sources = flight_symlink_sources(cask, source, true, staged_path, appdir)?;
if sources.len() != 1 {
return Err(eyre!("copy source glob must match exactly one path, got {}", sources.len()));
} Prevention
- Prefer literal paths for Copy steps; reserve globs for Symlink steps.
- Test the glob against the exact staged tree of each supported version.
- Keep glob patterns specific enough to match one artifact.
When it happens
Trigger: A FlightStep::Copy with source_glob=true whose pattern matches 0 or >=2 files inside staged_path (e.g. "*.app" matching two bundles, or a typo matching nothing). A non-glob Copy never hits this branch.
Common situations: Cask authors writing glob-based copy steps after an upstream app rename added a second matching bundle; version changes altering staged contents so a formerly unique glob now matches multiple files or none; copy-pasting a Symlink-style multi-match glob into a Copy step.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- brew-cask: structured symlink globs must use staged_path
- brew casks are installed at their current version ('{p}')
- brew-cask: requested token '{requested_token}' does not matc
- brew-cask: invalid {kind} '{value}'
- brew-cask: structured move with multiple sources requires a
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/cc6932cccb1d1de3.
Report an issue: GitHub.