jdx/mise · warning · eyre::Report
Homebrew now owns this cask
Error message
Homebrew now owns this cask
What it means
Thrown by mise's Homebrew-cask pruning path (validate_cask_prune_candidate, src/system/packages/brew/cask.rs:7248) when the Caskroom token directory being pruned now contains a `.metadata` directory. mise only prunes casks it installed itself (tracked via a `.mise-cask.toml` receipt); a `.metadata` dir means real Homebrew has since installed or claimed the same cask token. Deleting artifacts would break a brew-managed install, so the prune is refused and the cask is reported in the plan's skipped list instead of removed.
Solutions
- Decide which tool owns the cask: if Homebrew should own it, run `brew uninstall --cask <token>` or stop tracking it in mise config.
- If mise should own it, remove Homebrew's claim first with `brew uninstall --cask <token>` (this removes the .metadata dir), then re-run the mise prune.
- If the .metadata dir is a stale leftover from an aborted brew operation, verify with `brew list --cask`, then remove `<Caskroom>/<token>/.metadata` manually and re-run prune.
- Keep mise and Homebrew on separate prefixes so cask tokens never collide.
Example fix
# before mise prune # skip: "Homebrew now owns this cask" # after (mise should own the cask) brew uninstall --cask <token> mise prune
Defensive patterns
Strategy: validation
Validate before calling
// Check Homebrew's ownership marker before offering/pruning a cask
fn brew_owns_cask(caskroom: &std::path::Path, token: &str) -> bool {
caskroom.join(token).join(".metadata").symlink_metadata().is_ok()
}
if brew_owns_cask(&caskroom, &token) {
// skip: let brew manage this cask; do not prune
} Try / catch
Treat the Err as a skip, not an abort: `if let Err(reason) = validate_cask_prune_candidate(&candidate) { plan.skipped.push(CaskPruneSkip { token: candidate.token.clone(), reason: reason.to_string() }); continue; }` — exactly how cask.rs itself converts this refusal into a reported skip. Prevention
- Never let Homebrew and mise manage the same cask token under one prefix.
- When migrating a cask between brew and mise, uninstall it from the other tool first.
- Treat a .metadata dir in a Caskroom token dir as a hard ownership marker — never delete it to force pruning.
When it happens
Trigger: Running mise's package prune/uninstall for a cask token after `brew install --cask <same-token>` (or a brew upgrade/reinstall) created `$(brew --prefix)/Caskroom/<token>/.metadata`. Any call into cask_prune_plan/validate_cask_prune_candidate where homebrew_metadata_present(token) is true.
Common situations: User installs a cask with mise, later installs or reinstalls the same cask with native Homebrew, then asks mise to prune; migrating between brew and mise without cleaning the Caskroom; mise and Homebrew sharing one HOMEBREW_PREFIX so token dirs collide.
Related errors
- Homebrew now owns this cask
- metadata-only app ownership cannot be proven safely during…
- app target is outside an allowed Applications directory
- artifact target has changed
- artifact target is now claimed by another cask
AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22).
Data as JSON: /api/errors/60d06b02de75014f.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/packages/brew/cask.rs:7250
}
}
for target in &candidate.receipt.targets {
if claims
.get(&target.path)
.is_some_and(|tokens| tokens.iter().any(|token| token != &candidate.token))
{
bail!(
"artifact target is now claimed by another cask: {}",
target.path.display()
);
}
}
Ok(())
}
fn validate_cask_prune_candidate(candidate: &CaskPruneCandidate) -> Result<()> {
if homebrew_metadata_present(&candidate.token) {
bail!("Homebrew now owns this cask");
}
let receipt = &candidate.receipt;
if read_receipt(&candidate.version_dir)?.as_ref() != Some(receipt) {
bail!("ownership receipt has changed");
}
if receipt.schema_version != 3 || !receipt.prune_safe || !receipt.pkg_ids.is_empty() {
bail!("receipt is not marked safe for direct-artifact pruning");
}
if !receipt.metadata_only_apps.is_empty() {
bail!("metadata-only app ownership cannot be proven safely during pruning");
}
let records = receipt
.targets
.iter()
.map(|record| (record.path.clone(), record))
.collect::<BTreeMap<_, _>>();
let expected = receipt
.appsView on GitHub (pinned to 6f52dcdf99)