jdx/mise · error

brew-cask: invalid appdir

Error message

brew-cask: invalid appdir '{}'

What it means

This error is thrown by `binary_target_path` in src/system/packages/brew/cask/paths.rs when a cask declares a binary target under `$APPDIR/` but the `appdir` argument passed in is not one of the allowed application directories (the default ~/Applications, Homebrew's prefix Applications, or the configured target appdir). The library validates appdir against `allowed_appdir_roots()` to prevent binaries from being linked into arbitrary user-chosen directories. It only fires after the `$APPDIR/`-prefixed target has passed its own escape checks.

Solutions

  1. Set the cask's appdir to one of the allowed roots shown by checking `allowed_appdir_roots()`: the default app dir, `target_app_dir()`, or `$(brew --prefix)/Applications`
  2. Remove custom appdir overrides so the default app directory is used
  3. Normalize the appdir path (make it absolute, remove trailing slashes, resolve symlinks) so it compares equal to an allowed root
  4. If the allowed root itself is wrong (misdetected HOME or prefix), fix the environment/config that determines `target_app_dir()`

Example fix

// before
target_path(cask, "$APPDIR/MyApp.app", "/opt/myapps")
// error: brew-cask: invalid appdir '/opt/myapps'

// after
target_path(cask, "$APPDIR/MyApp.app", &default_appdir()) // an allowed root, e.g. ~/Applications
Defensive patterns

Strategy: validation

Validate before calling

fn appdir_is_allowed(appdir: &Path) -> bool {
    allowed_appdir_roots()
        .map(|roots| roots.iter().any(|root| root == appdir))
        .unwrap_or(false)
}

Type guard

fn is_valid_appdir(p: &Path) -> bool {
    p.is_absolute() && appdir_is_allowed(p)
}

Prevention

When it happens

Trigger: Calling `binary_target_path(target_name, appdir)` where target_name starts with `$APPDIR/` and `appdir` is not exactly equal to any path returned by `allowed_appdir_roots()` (default app dir, `target_app_dir()`, or `<prefix>/Applications`). This happens via `target_path` or `binary_targets_must_stay_under_an_allowed_root` when a cask's appdir value is a non-standard or mistyped directory.

Common situations: A cask formula specifies `appdir:` pointing to a custom folder; the user overrides the appdir setting with a relative or differently-cased path; an old config from a previous Homebrew prefix location; paths written with a trailing slash or symlink that don't compare byte-equal to the allowed roots.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/75f3d1f0f416bfea. Report an issue: GitHub.

Appendix: source

Thrown at src/system/packages/brew/cask/paths.rs:276

}

pub(super) fn allowed_binary_target_roots_display(roots: &[PathBuf]) -> String {
    roots
        .iter()
        .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

View on GitHub (pinned to 533346cc37)