jdx/mise · error · eyre::Report

brew-cask: $APPDIR must prefix a binary target

Error message

brew-cask: $APPDIR must prefix a binary target

What it means

The $APPDIR placeholder is only meaningful as a prefix: binary_target_path handles targets starting with "$APPDIR/" and rejects any other occurrence of the literal "$APPDIR" in a binary target. A placeholder buried mid-path ("bin/$APPDIR/tool") has no defined expansion point, so mise refuses it instead of linking to an unintended location.

Source

Thrown at src/system/packages/brew/cask.rs:6148

    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);
        if relative.components().next().is_none()
            || relative
                .components()
                .any(|component| !matches!(component, Component::Normal(_)))
        {
            bail!("brew-cask: binary $APPDIR target '{target_name}' must stay below Applications");
        }
        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 9dcfcaa0dc)

Solutions

  1. Move the placeholder to the front: "$APPDIR/tool" — or drop it and use a normal target relative to the brew prefix
  2. For prefix-anchored binaries use "$HOMEBREW_PREFIX/bin/tool" or plain "tool"
  3. Report the malformed stanza upstream if it comes from a tap

Example fix

// before (cask binary target)
"target": "bin/$APPDIR/tool"
// after
"target": "$APPDIR/tool"
Defensive patterns

Strategy: type-guard

Type guard

fn uses_appdir_prefix_correctly(name: &str) -> bool {
    match name.find("$APPDIR") {
        Some(pos) => name[pos..].starts_with("$APPDIR/"),
        None => true,
    }
}

Try / catch

match binary_target_path(name, &appdir) {
    Ok(p) => p,
    Err(e) if e.to_string().contains("$APPDIR must prefix") => {
        warn!("skipping malformed $APPDIR target: {name}");
        continue;
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: A cask binary artifact target containing "$APPDIR" anywhere other than the start, e.g. "bin/$APPDIR/tool" or "foo$APPDIR".

Common situations: Hand-written or templated casks misusing the placeholder; tap generators interpolating variables into the middle of target strings.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/23d91830a225fc94. Report an issue: GitHub.