{"record":{"id":"2d862e29a2191081","repo":"jdx/mise","slug":"brew-cask-invalid-appdir","errorCode":null,"errorMessage":"brew-cask: invalid appdir '{}'","messagePattern":"brew-cask: invalid appdir '(.+?)'","errorType":"validation","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask.rs","lineNumber":6143,"sourceCode":"        .collect::<Vec<_>>()\n        .join(\" or \")\n}\n\nfn 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        if relative.components().next().is_none()\n            || relative\n                .components()\n                .any(|component| !matches!(component, Component::Normal(_)))\n        {\n            bail!(\"brew-cask: binary $APPDIR target '{target_name}' must stay below Applications\");\n        }\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":6125,"sourceCodeEnd":6161,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/packages/brew/cask.rs#L6125-L6161","documentation":"For $APPDIR binary targets, mise cross-checks the appdir it was handed against allowed_appdir_roots() — [/Applications, target_app_dir() (the resolved MISE_BREW_CASK_OPT_APPDIR override), and <HOMEBREW_PREFIX>/Applications] (src/system/packages/brew/cask.rs:6107). This bail means the appdir argument is none of those, i.e. an internal inconsistency between how the caller computed the appdir and how the allowlist computes it (for example a non-canonicalized or differently-resolved path).","triggerScenarios":"binary_target_path called with an appdir that is not the default /Applications, the resolved env override, or <prefix>/Applications — e.g. the caller passed a symlinked or un-canonicalized spelling while the allowlist holds canonical paths.","commonSituations":"MISE_BREW_CASK_OPT_APPDIR pointing at a symlinked directory that resolves differently at plan time vs. link time; HOMEBREW_PREFIX changing mid-operation; custom code inside mise forks passing an arbitrary appdir.","solutions":["Unset MISE_BREW_CASK_OPT_APPDIR (or set it to a canonical, non-symlinked absolute dir) so the caller and allowlist agree","Run `realpath` on the override and export that resolved value","Ensure HOMEBREW_PREFIX is stable across the mise invocation","Update mise — mismatches like this are treated as bugs and fixed"],"exampleFix":"# before\nexport MISE_BREW_CASK_OPT_APPDIR=~/MyApps   # ~/MyApps is a symlink\n# after\nexport MISE_BREW_CASK_OPT_APPDIR=\"$(realpath ~/MyApps)\"","handlingStrategy":"validation","validationCode":"use std::path::PathBuf;\nfn appdir_in_allowlist(appdir: &std::path::Path, prefix: &std::path::Path) -> bool {\n    let mut roots = vec![PathBuf::from(\"/Applications\")];\n    for root in [appdir_resolved_from_env(), prefix.join(\"Applications\")] {\n        if !roots.contains(&root) { roots.push(root); }\n    }\n    roots.iter().any(|r| r == appdir)\n}","typeGuard":null,"tryCatchPattern":"match binary_target_path(name, &appdir) {\n    Ok(p) => p,\n    Err(e) if e.to_string().contains(\"invalid appdir\") => {\n        let canonical = appdir.canonicalize().unwrap_or(appdir.to_path_buf());\n        binary_target_path(name, &canonical)\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Keep MISE_BREW_CASK_OPT_APPDIR stable and canonical (realpath'd) for the whole mise run","Do not change HOMEBREW_PREFIX between planning and linking cask binaries"],"tags":["brew-cask","appdir","internal-consistency","env-var"],"backgroundTag":"invalid-env-var-value","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}