jdx/mise · error

app target is outside an allowed Applications directory

Error message

app target is outside an allowed Applications directory: {}

What it means

Before pruning a staged cask, mise verifies each app target recorded in the receipt is a Directory-kind target located under one of the allowed Applications directories (allowed_appdir_roots) and that the staged copy in the version dir matches the target's fingerprint. If the path is outside every allowed Applications root, or the staged app no longer matches, deletion is refused to avoid removing files mise does not provably own.

Solutions

  1. Verify the app still exists at the receipt path inside an allowed Applications directory; reinstall the cask with mise to regenerate the receipt for the current location
  2. If the app was customized via HOMEBREW_CASK_OPTS appdir, ensure the pruning mise instance sees the same appdir roots
  3. Replace or restore the modified .app so the staged fingerprint matches, then retry the prune
  4. Remove the cask manually or via brew instead of mise direct-artifact pruning

Example fix

// before: app relocated outside allowed roots, fingerprint mismatch
/Applications/Custom/Foo.app  // not below allowed_appdir_roots()
// after: reinstall so receipt matches actual location
mv /Applications/Custom/Foo.app /Applications/Foo.app
mise uninstall brew-cask:foo && mise install brew-cask:foo
Defensive patterns

Strategy: validation

Validate before calling

// pre-flight: confirm each app target is under an allowed Applications root
fn apps_in_allowed_roots(receipt: &CaskReceipt) -> Result<(), PathBuf> {
    let roots = allowed_appdir_roots()?;
    receipt.apps.iter().find(|p| !roots.iter().any(|r| path_is_below(p, r)))
        .map_or(Ok(()), Err)
}

Type guard

fn app_target_is_safe(path: &Path, roots: &[PathBuf]) -> bool {
    path.file_name().is_some()
        && path.metadata().map(|m| m.is_dir()).unwrap_or(false)
        && roots.iter().any(|root| path_is_below(path, root))
}

Try / catch

match apply_cask_prune_plan_in(&plan) {
    Ok(removed) => info!("pruned {removed} casks"),
    Err(e) if e.to_string().contains("outside an allowed Applications directory") => {
        warn!("app relocated or modified; reinstalling cask instead: {e:#}");
        // fall back to uninstall+install to regenerate the receipt
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling cask_prune_plan_from_tokens or apply_cask_prune_plan_in when receipt.apps contains a path not under an allowed Applications dir (e.g. a custom 'appdir:' install location), the target's fingerprint kind is not Directory, or the staged app in candidate.version_dir does not match the target record (app renamed, modified, or replaced).

Common situations: User moved the .app out of /Applications or installed with a custom appdir so the receipt path no longer matches allowed roots; the app was updated/modified in place so the staged fingerprint no longer matches; a receipt written under a different HOMEBREW_CASKROOM or appdir setting.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/d610a058ca9ca8c9. Report an issue: GitHub.

Appendix: source

Thrown at src/system/packages/brew/cask/state.rs:1076

        bail!("receipt target inventory is incomplete or duplicated");
    }
    if records.keys().any(|path| !expected.contains(path)) {
        bail!("receipt target inventory contains an unclassified path");
    }

    for path in &receipt.apps {
        let record = records
            .get(path)
            .ok_or_else(|| eyre!("missing app target record"))?;
        if record.fingerprint.kind != CaskTargetKind::Directory
            || !allowed_appdir_roots()?
                .iter()
                .any(|root| path_is_below(path, root))
            || !path.file_name().is_some_and(|name| {
                staged_app_matches_target(record, &candidate.version_dir.join(name))
            })
        {
            bail!(
                "app target is outside an allowed Applications directory: {}",
                path.display()
            );
        }
    }
    for path in &receipt.binaries {
        let record = records
            .get(path)
            .ok_or_else(|| eyre!("missing binary target record"))?;
        if record.fingerprint.kind != CaskTargetKind::Symlink
            || !allowed_binary_target_roots()
                .iter()
                .any(|root| path_is_below(path, root))
            || !symlink_resolves_below(path, &candidate.version_dir)
        {
            bail!(
                "binary target is not an owned Caskroom symlink: {}",
                path.display()

View on GitHub (pinned to 533346cc37)