{"record":{"id":"3b25aee934b15ecb","repo":"jdx/mise","slug":"brew-cask-binary-target-must-be-under","errorCode":null,"errorMessage":"brew-cask: binary target '{}' must be under {}","messagePattern":"brew-cask: binary target '(.+?)' must be under (.+?)","errorType":"validation","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask.rs","lineNumber":6172,"sourceCode":"    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 '..'\",\n            target.display()\n        );\n    }\n    let roots = allowed_binary_target_roots();\n    if !roots.iter().any(|root| target.starts_with(root)) {\n        bail!(\n            \"brew-cask: binary target '{}' must be under {}\",\n            target.display(),\n            allowed_binary_target_roots_display(&roots)\n        );\n    }\n    Ok(target)\n}\n\nfn installed_version(token: &str) -> Option<String> {\n    let versions = installed_versions(token);\n    match versions.as_slice() {\n        [version] => Some(version.clone()),\n        [] => None,\n        _ => {\n            warn!(\"brew-cask:{token}: multiple Caskroom versions found; reinstall to reconcile\");\n            None\n        }\n    }","sourceCodeStart":6154,"sourceCodeEnd":6190,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/packages/brew/cask.rs#L6154-L6190","documentation":"After expansion and anchoring, a binary target must fall under an allowed root: allowed_binary_target_roots() returns the Homebrew prefix plus /usr/local (src/system/packages/brew/cask.rs:6097). This bail fires for absolute targets pointing anywhere else — mise deliberately refuses to link cask binaries into system locations like /usr/bin or /Library.","triggerScenarios":"A cask binary target that is absolute and outside <HOMEBREW_PREFIX> and /usr/local, e.g. \"/usr/bin/tool\" or \"/opt/custom/bin/tool\"; or a non-standard HOMEBREW_PREFIX that changes which roots are allowed.","commonSituations":"Casks declaring system-path binaries; users with custom Homebrew prefixes on Apple Silicon (/opt/homebrew) hitting casks written for /usr/local; third-party taps with unusual targets.","solutions":["Change the binary target to a prefix-relative form: bare \"tool\" (lands in <prefix>/bin) or \"$HOMEBREW_PREFIX/bin/tool\"","Check `echo $HOMEBREW_PREFIX` — the allowed roots are exactly that prefix plus /usr/local","Report the target upstream if a published cask declares an out-of-root binary","Install the cask via `brew install --cask` if you need Homebrew's own (looser) behavior"],"exampleFix":"// before (cask binary target)\n\"target\": \"/usr/bin/mytool\"\n// after\n\"target\": \"mytool\"","handlingStrategy":"validation","validationCode":"use std::path::{Path, PathBuf};\nfn binary_target_within_roots(name: &str, prefix: &Path) -> bool {\n    let expanded = name.replace(\"$HOMEBREW_PREFIX\", &prefix.to_string_lossy());\n    let path = PathBuf::from(&expanded);\n    let target = if path.is_absolute() {\n        path\n    } else if expanded.contains('/') {\n        prefix.join(path)\n    } else {\n        prefix.join(\"bin\").join(path)\n    };\n    let mut roots = vec![prefix.to_path_buf()];\n    let usr_local = PathBuf::from(\"/usr/local\");\n    if prefix != usr_local { roots.push(usr_local); }\n    roots.iter().any(|r| target.starts_with(r))\n}","typeGuard":null,"tryCatchPattern":"match binary_target_path(name, &appdir) {\n    Ok(p) => p,\n    Err(e) if e.to_string().contains(\"must be under\") => {\n        warn!(\"cask binary target outside allowed roots: {name}\");\n        continue;\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Keep binary targets under the Homebrew prefix or /usr/local — bare names land in <prefix>/bin automatically","Check echo $HOMEBREW_PREFIX when authoring casks for non-default prefixes (e.g. /opt/homebrew)"],"tags":["brew-cask","path-containment","binary","homebrew-prefix"],"backgroundTag":"path-containment-violation","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}