jdx/mise · error · eyre::Report
brew-cask: binary target '{}' must be under {}
Error message
brew-cask: binary target '{}' must be under {} What it means
After expansion and anchoring, a binary target must fall under an allowed root: allowed_binary_target_roots() returns the Homebrew prefix plus /usr/local (src/system/packages/brew/cask.rs:6097). This bail fires for absolute targets pointing anywhere else — mise deliberately refuses to link cask binaries into system locations like /usr/bin or /Library.
Source
Thrown at src/system/packages/brew/cask.rs:6172
let target = if path.is_absolute() {
path
} else if target_name.contains('/') {
prefix.join(path)
} else {
prefix.join("bin").join(path)
};
if target
.components()
.any(|component| matches!(component, Component::ParentDir))
{
bail!(
"brew-cask: binary target '{}' must not contain '..'",
target.display()
);
}
let roots = allowed_binary_target_roots();
if !roots.iter().any(|root| target.starts_with(root)) {
bail!(
"brew-cask: binary target '{}' must be under {}",
target.display(),
allowed_binary_target_roots_display(&roots)
);
}
Ok(target)
}
fn installed_version(token: &str) -> Option<String> {
let versions = installed_versions(token);
match versions.as_slice() {
[version] => Some(version.clone()),
[] => None,
_ => {
warn!("brew-cask:{token}: multiple Caskroom versions found; reinstall to reconcile");
None
}
}View on GitHub (pinned to 9dcfcaa0dc)
Solutions
- Change the binary target to a prefix-relative form: bare "tool" (lands in <prefix>/bin) or "$HOMEBREW_PREFIX/bin/tool"
- Check `echo $HOMEBREW_PREFIX` — the allowed roots are exactly that prefix plus /usr/local
- Report the target upstream if a published cask declares an out-of-root binary
- Install the cask via `brew install --cask` if you need Homebrew's own (looser) behavior
Example fix
// before (cask binary target) "target": "/usr/bin/mytool" // after "target": "mytool"
Defensive patterns
Strategy: validation
Validate before calling
use std::path::{Path, PathBuf};
fn binary_target_within_roots(name: &str, prefix: &Path) -> bool {
let expanded = name.replace("$HOMEBREW_PREFIX", &prefix.to_string_lossy());
let path = PathBuf::from(&expanded);
let target = if path.is_absolute() {
path
} else if expanded.contains('/') {
prefix.join(path)
} else {
prefix.join("bin").join(path)
};
let mut roots = vec![prefix.to_path_buf()];
let usr_local = PathBuf::from("/usr/local");
if prefix != usr_local { roots.push(usr_local); }
roots.iter().any(|r| target.starts_with(r))
} Try / catch
match binary_target_path(name, &appdir) {
Ok(p) => p,
Err(e) if e.to_string().contains("must be under") => {
warn!("cask binary target outside allowed roots: {name}");
continue;
}
Err(e) => Err(e),
} Prevention
- Keep binary targets under the Homebrew prefix or /usr/local — bare names land in <prefix>/bin automatically
- Check echo $HOMEBREW_PREFIX when authoring casks for non-default prefixes (e.g. /opt/homebrew)
When it happens
Trigger: A cask binary target that is absolute and outside <HOMEBREW_PREFIX> and /usr/local, e.g. "/usr/bin/tool" or "/opt/custom/bin/tool"; or a non-standard HOMEBREW_PREFIX that changes which roots are allowed.
Common situations: Casks declaring system-path binaries; users with custom Homebrew prefixes on Apple Silicon (/opt/homebrew) hitting casks written for /usr/local; third-party taps with unusual targets.
Related errors
- brew-cask: refusing generic artifact copy outside Homebrew p
- brew-cask: generic artifact target '{}' must stay below {}
- brew-cask: refusing to remove generic artifact outside {}: {
- brew-cask: app target '{target_name}' must be under {}
- brew-cask: {APP_DIR_ENV} '{}' must not resolve to the filesy
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/3b25aee934b15ecb.
Report an issue: GitHub.