{"record":{"id":"6a0babd9253e108d","repo":"jdx/mise","slug":"app-target-is-outside-an-allowed-applications-dire","errorCode":null,"errorMessage":"app target is outside an allowed Applications directory: {}","messagePattern":"app target is outside an allowed Applications directory: (.+?)","errorType":"exception","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask.rs","lineNumber":7050,"sourceCode":"        bail!(\"receipt target inventory is incomplete or duplicated\");\n    }\n    if records.keys().any(|path| !expected.contains(path)) {\n        bail!(\"receipt target inventory contains an unclassified path\");\n    }\n\n    for path in &receipt.apps {\n        let record = records\n            .get(path)\n            .ok_or_else(|| eyre!(\"missing app target record\"))?;\n        if record.fingerprint.kind != CaskTargetKind::Directory\n            || !allowed_appdir_roots()?\n                .iter()\n                .any(|root| path_is_below(path, root))\n            || !path.file_name().is_some_and(|name| {\n                staged_app_matches_target(record, &candidate.version_dir.join(name))\n            })\n        {\n            bail!(\n                \"app target is outside an allowed Applications directory: {}\",\n                path.display()\n            );\n        }\n    }\n    for path in &receipt.binaries {\n        let record = records\n            .get(path)\n            .ok_or_else(|| eyre!(\"missing binary target record\"))?;\n        if record.fingerprint.kind != CaskTargetKind::Symlink\n            || !allowed_binary_target_roots()\n                .iter()\n                .any(|root| path_is_below(path, root))\n            || !symlink_resolves_below(path, &candidate.version_dir)\n        {\n            bail!(\n                \"binary target is not an owned Caskroom symlink: {}\",\n                path.display()","sourceCodeStart":7032,"sourceCodeEnd":7068,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/packages/brew/cask.rs#L7032-L7068","documentation":"App-bundle targets must be directories located below one of the allowed Applications roots (default /Applications plus any configured appdir), and the staged copy inside the Caskroom version directory must fingerprint-match the installed bundle. This guard stops the prune from deleting .app bundles that were moved, replaced, or installed somewhere the tool never sanctioned.","triggerScenarios":"validate_cask_prune_candidate, apps loop: record.fingerprint.kind != Directory, path not below any allowed_appdir_roots() entry, or staged_app_matches_target fails against version_dir/<app-name>. Typical causes: the appdir setting changed after install, the user dragged the app out of /Applications, or the staged copy in Caskroom was deleted.","commonSituations":"Installing with a custom appdir (e.g. ~/Applications) and later removing that setting; user moved the app to another folder; app updated itself in place; staged Caskroom copy removed to save space.","solutions":["Restore the appdir configuration the cask was installed with so the app's location is an allowed root again","Move the app bundle back under an allowed Applications directory and ensure Caskroom/<token>/<version>/<name>.app still matches it","Reinstall the cask (regenerates both staged copy and receipt), then prune","If the app is intentionally elsewhere, uninstall the cask explicitly instead of pruning"],"exampleFix":"# before: app installed to ~/Applications via appdir, setting later removed\n# receipt.apps = [\"/Users/me/Applications/Thing.app\"] -> not below allowed roots\n\n# after: re-add the appdir before pruning\nbrew.appdir = '~/Applications'   # restore in config\nmise system prune","handlingStrategy":"validation","validationCode":"// Before pruning, confirm each app sits below an allowed root and the staged\n// copy matches (the same predicate the validator uses).\nfn app_ok(candidate: &CaskPruneCandidate, path: &Path) -> Result<bool> {\n    let Some(name) = path.file_name() else { return Ok(false) };\n    Ok(allowed_appdir_roots()?.iter().any(|root| path_is_below(path, root))\n        && staged_app_matches_target(\n            &records[candidate].find(path).unwrap(),\n            &candidate.version_dir.join(name),\n        ))\n}","typeGuard":"fn app_path_allowed(path: &Path, roots: &[PathBuf]) -> bool {\n    roots.iter().any(|root| path.strip_prefix(root).is_ok_and(|rel| rel.components().next().is_some()))\n}","tryCatchPattern":"// Per-cask skip; never abort the prune run because one app bundle moved.\nif let Err(reason) = validate_cask_prune_candidate(candidate) {\n    warn!(\"brew-cask:{}: skipped: {reason:#}\", candidate.token);\n    continue;\n}","preventionTips":["Keep the appdir setting stable across install and prune","Leave .app bundles where the cask installed them; relocate via reinstall, not drag-and-drop","Do not delete staged copies inside Caskroom/<token>/<version>/"],"tags":["brew","cask","prune","appdir","macos","path-allowlist"],"backgroundTag":"path-allowlist-violation","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}