{"record":{"id":"5e8809048b7df93d","repo":"jdx/mise","slug":"brew-cask-appdir-must-prefix-a-binary-target-5e8809","errorCode":null,"errorMessage":"brew-cask: $APPDIR must prefix a binary target","messagePattern":"brew-cask: \\$APPDIR must prefix a binary target","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/paths.rs","lineNumber":281,"sourceCode":"        .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\n        .components()\n        .any(|component| matches!(component, Component::ParentDir))\n    {\n        bail!(\n            \"brew-cask: binary target '{}' must not contain '..'\",","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/packages/brew/cask/paths.rs#L263-L299","documentation":"In a cask `binary` target, `$APPDIR` is only meaningful as a path prefix (`$APPDIR/...`). If the string contains `$APPDIR` anywhere else — mid-path, as a filename, or embedded — `binary_target_path` cannot resolve it safely and rejects it, since substituting the appdir in arbitrary positions could produce paths outside intended roots.","triggerScenarios":"Calling `target_path`/`binary_targets_must_stay_under_an_allowed_root` with a binary target like `bin/$APPDIR-tool`, `tools/$APPDIR/sub/tool` (without `$APPDIR/` at the start), or `my$APPDIRapp`, i.e. any `contains(\"$APPDIR\")` hit after the `$APPDIR/` prefix case was not taken.","commonSituations":"Hand-written cask that misspells the prefix (missing slash or leading components before `$APPDIR`); macro/templating that injects the placeholder mid-string; copying app-target syntax into binary stanzas.","solutions":["Rewrite the target so `$APPDIR` is at the very start followed by `/`: `$APPDIR/bin/tool`","If the binary belongs in the Homebrew prefix, use `$HOMEBREW_PREFIX/bin/tool` or just `tool` (placed in `bin`) instead of `$APPDIR`","Remove the accidental `$APPDIR` substring if it was a typo in the target name","Use an absolute path under an allowed binary root if a fixed location is required"],"exampleFix":"// before (cask stanza)\nbinary: [['scripts/tool', { target: 'opt/$APPDIR/tool' }]]\n// after\nbinary: [['scripts/tool', { target: '$APPDIR/tool' }]]","handlingStrategy":"validation","validationCode":"fn appdir_target_ok(t: &str) -> bool {\n    if t.contains(\"$APPDIR\") {\n        t.starts_with(\"$APPDIR/\")\n    } else {\n        true\n    }\n}","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"$APPDIR must prefix a binary target\") => {\n        eprintln!(\"move $APPDIR to the start of the target: $APPDIR/...\");\n    }\n    r => r?,\n}","preventionTips":["Use $APPDIR only as a leading prefix followed by '/'","Do not template $APPDIR into the middle of a target string","Use $HOMEBREW_PREFIX for prefix-relative binaries instead","Lint cask stanzas for placeholder placement"],"tags":["path-validation","brew-cask","placeholder-syntax"],"backgroundTag":"invalid-argument-format","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}