jdx/mise · error
brew-cask: $APPDIR must prefix a binary target
Error message
brew-cask: $APPDIR must prefix a binary target
What it means
In a cask `binary` target, `$APPDIR` is only meaningful as a path prefix (`$APPDIR/...`). If the string contains `$APPDIR` anywhere else — mid-path, as a filename, or embedded — `binary_target_path` cannot resolve it safely and rejects it, since substituting the appdir in arbitrary positions could produce paths outside intended roots.
Source
Thrown at src/system/packages/brew/cask/paths.rs:281
.map(|root| root.display().to_string())
.collect::<Vec<_>>()
.join(" or ")
}
pub(super) fn binary_target_path(target_name: &str, appdir: &Path) -> Result<PathBuf> {
if target_name.contains('\0') {
bail!("brew-cask: binary target contains NUL");
}
if let Some(relative) = target_name.strip_prefix("$APPDIR/") {
let relative = Path::new(relative);
reject_appdir_escape(relative, "binary $APPDIR target", target_name)?;
if !allowed_appdir_roots()?.iter().any(|root| root == appdir) {
bail!("brew-cask: invalid appdir '{}'", appdir.display());
}
return Ok(appdir.join(relative));
}
if target_name.contains("$APPDIR") {
bail!("brew-cask: $APPDIR must prefix a binary target");
}
let prefix = prefix::prefix();
let prefix_str = prefix.to_string_lossy();
let target_name = target_name.replace("$HOMEBREW_PREFIX", prefix_str.as_ref());
let path = PathBuf::from(&target_name);
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 '..'",View on GitHub (pinned to afd2eddd3a)
Solutions
- Rewrite the target so `$APPDIR` is at the very start followed by `/`: `$APPDIR/bin/tool`
- If the binary belongs in the Homebrew prefix, use `$HOMEBREW_PREFIX/bin/tool` or just `tool` (placed in `bin`) instead of `$APPDIR`
- Remove the accidental `$APPDIR` substring if it was a typo in the target name
- Use an absolute path under an allowed binary root if a fixed location is required
Example fix
// before (cask stanza)
binary: [['scripts/tool', { target: 'opt/$APPDIR/tool' }]]
// after
binary: [['scripts/tool', { target: '$APPDIR/tool' }]] Defensive patterns
Strategy: validation
Validate before calling
fn appdir_target_ok(t: &str) -> bool {
if t.contains("$APPDIR") {
t.starts_with("$APPDIR/")
} else {
true
}
} Try / catch
match result {
Err(e) if e.to_string().contains("$APPDIR must prefix a binary target") => {
eprintln!("move $APPDIR to the start of the target: $APPDIR/...");
}
r => r?,
} Prevention
- Use $APPDIR only as a leading prefix followed by '/'
- Do not template $APPDIR into the middle of a target string
- Use $HOMEBREW_PREFIX for prefix-relative binaries instead
- Lint cask stanzas for placeholder placement
When it happens
Trigger: Calling `target_path`/`binary_targets_must_stay_under_an_allowed_root` with a binary target like `bin/$APPDIR-tool`, `tools/$APPDIR/sub/tool` (without `$APPDIR/` at the start), or `my$APPDIRapp`, i.e. any `contains("$APPDIR")` hit after the `$APPDIR/` prefix case was not taken.
Common situations: Hand-written cask that misspells the prefix (missing slash or leading components before `$APPDIR`); macro/templating that injects the placeholder mid-string; copying app-target syntax into binary stanzas.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- brew-cask: refusing generic artifact copy outside Homebrew p
- brew-cask: invalid generic artifact parent
- brew-cask: generic artifact target '{}' must stay below {}
- brew-cask: invalid font target '{}'
- brew-cask: invalid structured flight path '{}'
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/5e8809048b7df93d.
Report an issue: GitHub.