jdx/mise · error

brew-cask:{}: Homebrew took ownership of this cask while ins

Error message

brew-cask:{}: Homebrew took ownership of this cask while installation was pending

What it means

mise re-checks Homebrew ownership at the last moment: after downloading and staging the cask payload (under the caskroom lock), it looks again for the `.metadata` marker. If it appeared since the first check, a concurrent `brew install --cask` claimed the cask mid-flight; mise deletes its staged payload and aborts rather than clobber brew's install — a TOCTOU guard.

Source

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

            for completion in &artifacts.completions {
                miseprintln!(
                    "install {} completion {}",
                    completion.shell.name(),
                    completion.source
                );
            }
            for generated in &artifacts.generated_completions {
                miseprintln!("generate completions from {}", generated.executable);
            }
            return Ok(cask.version);
        }
        prefix::bootstrap(false)?;
        let stage = fetch_and_stage(&cask, pr).await?;
        let _caskroom_lock = lock_caskroom()?;
        recover_flight_backups()?;
        if homebrew_metadata_present(&cask.token) {
            file::remove_all(&stage)?;
            bail!(
                "brew-cask:{}: Homebrew took ownership of this cask while installation was pending",
                cask.token
            );
        }
        if installed_cask_version(&cask, &artifacts)?.as_deref() == Some(cask.version.as_str()) {
            file::remove_all(stage)?;
            return Ok(cask.version);
        }
        let previous_binaries = previous_binary_targets(&cask)?;
        let previous_fonts = previous_font_targets(&cask)?;
        let previous_completions = previous_completion_targets(&cask)?;
        let previous_flight_symlinks = previous_flight_symlink_targets(&cask)?;
        let previous_flight_directories = previous_flight_directory_targets(&cask)?;
        let previous_generic = previous_generic_targets(&cask)?;
        let caskroom_token = caskroom_token_dir(&cask.token);
        let caskroom = caskroom_version_dir(&cask.token, &cask.version);
        let tmp_caskroom = caskroom_tmp_dir(&cask);
        file::remove_all(&tmp_caskroom)?;

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Let the other installation finish, verify with `brew list --cask`, then re-run the mise command — mise will detect the cask as installed
  2. Serialize installs: never run brew and mise cask installs concurrently for the same token
Defensive patterns

Strategy: retry

Validate before calling

// Right before retrying, confirm whether the concurrent brew install finished:
fn cask_installed_by_homebrew(token: &str) -> bool {
    std::path::Path::new(&caskroom(token)).join(".metadata").symlink_metadata().is_ok()
}

Try / catch

Catch the 'took ownership while installation was pending' bail, wait for any concurrent brew process to exit, then re-run the mise install — the idempotent path will then report already-installed or proceed cleanly.

Prevention

When it happens

Trigger: Running mise install/bootstrap for a cask while another process (brew CLI, another mise invocation, a CI step) installs the same cask; the marker appears between mise's pre-check and the post-staging re-check.

Common situations: Bootstrap scripts racing brew one-liners in dotfiles; parallel CI steps installing the same GUI tool; two mise processes started concurrently.

Related errors


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