jdx/mise · error

brew-cask: refusing elevated operation because target appear

Error message

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

What it means

Elevated installs are only allowed to create targets, never overwrite: right before the privileged rename/copy, mise stats the target (ensure_target_absent). If anything — file, symlink, or directory — now exists where the plan assumed absence, the operation is refused. This closes the TOCTOU window where a swapped-in symlink at the target could redirect a privileged write.

Source

Thrown at src/system/packages/brew/cask.rs:1912

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()),
    }
}

#[cfg(unix)]
fn copy_cask_artifact_at<Fd: std::os::fd::AsFd>(
    from: &Path,
    parent: Fd,
    name: &std::ffi::OsStr,
) -> Result<()> {
    use std::os::unix::fs::PermissionsExt;

    let metadata = from.symlink_metadata()?;
    let mode = nix::sys::stat::Mode::from_bits_truncate(
        metadata.permissions().mode() as nix::libc::mode_t

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Identify what created the target (path is in the error); if it came from a concurrent install, let it finish and re-run mise — it will detect the existing version
  2. If the target is stale, remove it deliberately (verify it is not a symlink someone else planted) and retry
  3. Serialize installers so two privileged writers never race the same path
Defensive patterns

Strategy: try-catch

Validate before calling

// Best-effort pre-check (still racy — the library re-checks atomically):
fn target_absent(target: &Path) -> bool {
    matches!(target.symlink_metadata(), Err(e) if e.kind() == std::io::ErrorKind::NotFound)
}

Try / catch

Catch 'refusing elevated operation because target appeared'; verify the appeared target's provenance (lstat it, check owner and symlink-ness), remove it if it is your own stale artifact, then retry. Never blindly unlink — the whole point of the guard is that something raced you.

Prevention

When it happens

Trigger: Something creates the target path between mise's earlier planning and the elevated step: a concurrent installer (brew, another mise run, a package installer), a login script, or an attacker racing the install.

Common situations: Parallel bootstrap scripts installing the same binary; re-running mise while a previous run's elevated step is still finishing; dotfiles daemons that continuously recreate symlinks.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/019fceebf5881236. Report an issue: GitHub.