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

  1. Rewrite the target so `$APPDIR` is at the very start followed by `/`: `$APPDIR/bin/tool`
  2. If the binary belongs in the Homebrew prefix, use `$HOMEBREW_PREFIX/bin/tool` or just `tool` (placed in `bin`) instead of `$APPDIR`
  3. Remove the accidental `$APPDIR` substring if it was a typo in the target name
  4. 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

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


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/5e8809048b7df93d. Report an issue: GitHub.