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
- 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`
- Remove custom appdir overrides so the default app directory is used
- Normalize the appdir path (make it absolute, remove trailing slashes, resolve symlinks) so it compares equal to an allowed root
- 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
- Always pass an appdir from `allowed_appdir_roots()`, never a user-typed string
- Normalize the appdir (absolute, no trailing slash) before comparing
- Test cask stanzas that use `$APPDIR` targets against both default and prefix appdirs
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
- brew-cask: binary target
- brew-cask: ' ' must be an absolute path
- brew-cask: ' ' must not resolve to the filesystem root
- brew-cask: app target contains NUL
- brew-cask: app target
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 targetView on GitHub (pinned to 533346cc37)