jdx/mise · error

brew-cask: refusing elevated operation because target appear

Error message

brew-cask: refusing elevated operation because target appeared: {}

What it means

This error is a TOCTOU (time-of-check-time-of-use) safety guard in `ensure_target_absent`. Before performing an elevated operation against a path, the function verifies the path does not exist via `symlink_metadata`; if a file, directory, or symlink has appeared at the target between the initial check and the elevated action, the operation is aborted rather than clobbering an object an attacker may have planted. It protects against symlink-swap races where a malicious local process creates a link at the destination path mid-operation.

Source

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

fn strict_elevated_directory_is_trusted(
    directory: &Path,
    stable_prefix: &Path,
    uid: u32,
    mode: u32,
) -> bool {
    uid == 0
        && mode & 0o002 == 0
        // Intel Homebrew conventionally uses root:admin 0775 for /usr/local.
        // Permit that exact prefix, but require every descendant and every
        // other ancestor used by the elevated operation to be non-writable.
        && (mode & 0o020 == 0 || directory == stable_prefix)
}

#[cfg(unix)]
fn ensure_target_absent(target: &Path) -> Result<()> {
    match target.symlink_metadata() {
        Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(()),
        Ok(_) => bail!(
            "brew-cask: refusing elevated operation because target appeared: {}",
            target.display()
        ),
        Err(err) => Err(err.into()),
    }
}

/// Opens a directory relative to `parent`, never following symlinks.
#[cfg(unix)]
fn open_dir_nofollow_at<Fd: std::os::fd::AsFd, P: nix::NixPath + ?Sized>(
    parent: Fd,
    name: &P,
) -> Result<std::os::fd::OwnedFd> {
    Ok(nix::fcntl::openat(
        parent,
        name,
        nix::fcntl::OFlag::O_RDONLY
            | nix::fcntl::OFlag::O_DIRECTORY

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Inspect the path named in the message (`target.display()`); if it is a leftover from an earlier failed run and nothing depends on it, remove the file/symlink and retry.
  2. Verify no other brew or cask process is running concurrently against the same prefix; serialize operations.
  3. If the target is legitimately expected to exist, this operation is intentionally refused — restructure the call so the path is clear, or use a non-elevated path that handles existing targets.
  4. If the path reappears on every attempt, check for background jobs (launchd/cron, other package managers) recreating it.
Defensive patterns

Strategy: validation

Validate before calling

use std::path::Path;
fn target_is_clear(target: &Path) -> bool {
    matches!(target.symlink_metadata(), Err(e) if e.kind() == std::io::ErrorKind::NotFound)
}
// before calling the elevated operation:
if !target_is_clear(&target) { eprintln!("target exists: {}", target.display()); }

Type guard

fn path_is_absent(p: &Path) -> bool {
    p.symlink_metadata().map_err(|e| e.kind() == std::io::ErrorKind::NotFound).unwrap_or(false)
}

Prevention

When it happens

Trigger: Calling a privileged brew-cask operation that resolves through `ensure_target_absent` when `target.symlink_metadata()` returns `Ok` — i.e. the target path (or a symlink at that path) exists at the moment of the re-check, typically because another process created it concurrently or a stale file from a previous failed run is present.

Common situations: A previous interrupted install left a file or dangling symlink at the cask artifact destination; a parallel brew/cask process racing on the same path; a malicious or accidental symlink planted at the destination; Nix/tmpfs state carried over between runs.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/69fd819feea68b8d. Report an issue: GitHub.