{"record":{"id":"75f3d1f0f416bfea","repo":"jdx/mise","slug":"brew-cask-invalid-appdir-75f3d1","errorCode":null,"errorMessage":"brew-cask: invalid appdir '{}'","messagePattern":"brew-cask: invalid appdir '(.+?)'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/paths.rs","lineNumber":276,"sourceCode":"}\n\npub(super) fn allowed_binary_target_roots_display(roots: &[PathBuf]) -> String {\n    roots\n        .iter()\n        .map(|root| root.display().to_string())\n        .collect::<Vec<_>>()\n        .join(\" or \")\n}\n\npub(super) fn binary_target_path(target_name: &str, appdir: &Path) -> Result<PathBuf> {\n    if target_name.contains('\\0') {\n        bail!(\"brew-cask: binary target contains NUL\");\n    }\n    if let Some(relative) = target_name.strip_prefix(\"$APPDIR/\") {\n        let relative = Path::new(relative);\n        reject_appdir_escape(relative, \"binary $APPDIR target\", target_name)?;\n        if !allowed_appdir_roots()?.iter().any(|root| root == appdir) {\n            bail!(\"brew-cask: invalid appdir '{}'\", appdir.display());\n        }\n        return Ok(appdir.join(relative));\n    }\n    if target_name.contains(\"$APPDIR\") {\n        bail!(\"brew-cask: $APPDIR must prefix a binary target\");\n    }\n    let prefix = prefix::prefix();\n    let prefix_str = prefix.to_string_lossy();\n    let target_name = target_name.replace(\"$HOMEBREW_PREFIX\", prefix_str.as_ref());\n    let path = PathBuf::from(&target_name);\n    let target = if path.is_absolute() {\n        path\n    } else if target_name.contains('/') {\n        prefix.join(path)\n    } else {\n        prefix.join(\"bin\").join(path)\n    };\n    if target","sourceCodeStart":258,"sourceCodeEnd":294,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/packages/brew/cask/paths.rs#L258-L294","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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()`"],"exampleFix":"// before\ntarget_path(cask, \"$APPDIR/MyApp.app\", \"/opt/myapps\")\n// error: brew-cask: invalid appdir '/opt/myapps'\n\n// after\ntarget_path(cask, \"$APPDIR/MyApp.app\", &default_appdir()) // an allowed root, e.g. ~/Applications","handlingStrategy":"validation","validationCode":"fn appdir_is_allowed(appdir: &Path) -> bool {\n    allowed_appdir_roots()\n        .map(|roots| roots.iter().any(|root| root == appdir))\n        .unwrap_or(false)\n}","typeGuard":"fn is_valid_appdir(p: &Path) -> bool {\n    p.is_absolute() && appdir_is_allowed(p)\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["path-validation","brew-cask","invalid-config-value"],"backgroundTag":"invalid-config-value","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}