jdx/mise · error

brew-cask: installer executable

Error message

brew-cask: installer executable '{}' was not found

What it means

mise's brew-cask backend refuses to run a cask installer when the resolved installer executable does not exist on disk as a regular file. The backend resolves the executable path from the cask's staged installer stanza and validates it before invoking it, so a missing or non-file path (directory, broken symlink, absent artifact) aborts installation instead of spawning a failing command. This protects against casks whose staged layout does not match what the cask formula declares.

Solutions

  1. Re-run the cask install with a clean staging area so the artifact is re-downloaded and fully extracted (e.g. remove the cached download and reinstall the cask).
  2. Verify the executable path in the cask formula actually exists in the staged bundle and is a regular file.
  3. Check for a broken symlink at the expected path and repair the source of that link.
  4. Update the cask/mise backend definitions, then retry; if the cask upstream changed its installer name, the formula must be updated.
  5. Ensure the filesystem hosting the staging directory is mounted and writable (not an unmounted or read-only volume).

Example fix

// before (formula references old name)
installer { script: { executable: "OldInstaller.app/Contents/MacOS/OldInstaller" } }
// after
installer { script: { executable: "NewInstaller.app/Contents/MacOS/NewInstaller" } }
Defensive patterns

Strategy: validation

Validate before calling

let exe = std::path::Path::new(&installer.executable);
if !exe.is_file() {
    eprintln!("installer executable missing: {}", exe.display());
    std::process::exit(1);
}

Type guard

fn is_regular_file(p: &std::path::Path) -> bool {
    p.is_file()
}

Prevention

When it happens

Trigger: Installing a cask whose installer stanza points to an executable that was never staged (bad cask formula), the staged artifact was pruned or partially extracted, the executable path resolves to a directory or dangling symlink, or the install ran with a changed staging directory/version mismatch so the expected binary is absent.

Common situations: Homebrew cask updates where the upstream project renamed its installer binary; interrupted or partial downloads; running mise against a cask whose staged files were manually cleaned from ~/Library/Caches or the brew cache; macOS version changes altering bundle layout.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at src/system/packages/brew/cask/mod.rs:2388

    // `allow_current_user` is true because a per-user appdir such as
    // `~/Applications` is legitimately owned by the invoking user.
    open_trusted_directory(Path::new("/"), relative, true, true)
}

fn run_installer_artifact(
    stage: &Path,
    installer: &InstallerArtifact,
    copied_files: &BTreeSet<PathBuf>,
) -> Result<()> {
    let executable = stage.join(&installer.executable);
    if staged_relative_path(stage, &executable).is_none() {
        bail!(
            "brew-cask: refusing installer executable outside trusted installer roots: {}",
            executable.display()
        );
    }
    if !executable.is_file() {
        bail!(
            "brew-cask: installer executable '{}' was not found",
            installer.executable
        );
    }
    let executable = file::desymlink_path(&executable);
    if !executable.starts_with(file::desymlink_path(stage)) && !copied_files.contains(&executable) {
        bail!(
            "brew-cask: refusing installer executable outside trusted installer roots: {}",
            executable.display()
        );
    }
    file::make_executable(&executable)?;
    let prefix = prefix::prefix();
    let mut paths = vec![prefix.join("bin"), prefix.join("sbin")];
    if let Some(path) = std::env::var_os("PATH") {
        paths.extend(std::env::split_paths(&path));
    }
    let path = std::env::join_paths(paths)?;

View on GitHub (pinned to 533346cc37)