{"record":{"id":"739397d4f50bf4cf","repo":"jdx/mise","slug":"artifact-target-is-now-claimed-by-another-cask","errorCode":null,"errorMessage":"artifact target is now claimed by another cask: {}","messagePattern":"artifact target is now claimed by another cask: (.+?)","errorType":"exception","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask.rs","lineNumber":6995,"sourceCode":"            let version = version?;\n            if !version.file_type()?.is_dir()\n                || version.file_name().to_string_lossy().starts_with('.')\n            {\n                continue;\n            }\n            if let Some(receipt) = read_receipt(&version.path())? {\n                for target in receipt.targets {\n                    claims.entry(target.path).or_default().insert(token.clone());\n                }\n            }\n        }\n    }\n    for target in &candidate.receipt.targets {\n        if claims\n            .get(&target.path)\n            .is_some_and(|tokens| tokens.iter().any(|token| token != &candidate.token))\n        {\n            bail!(\n                \"artifact target is now claimed by another cask: {}\",\n                target.path.display()\n            );\n        }\n    }\n    Ok(())\n}\n\nfn validate_cask_prune_candidate(candidate: &CaskPruneCandidate) -> Result<()> {\n    if homebrew_metadata_present(&candidate.token) {\n        bail!(\"Homebrew now owns this cask\");\n    }\n    let receipt = &candidate.receipt;\n    if read_receipt(&candidate.version_dir)?.as_ref() != Some(receipt) {\n        bail!(\"ownership receipt has changed\");\n    }\n    if receipt.schema_version != 3 || !receipt.prune_safe || !receipt.pkg_ids.is_empty() {\n        bail!(\"receipt is not marked safe for direct-artifact pruning\");","sourceCodeStart":6977,"sourceCodeEnd":7013,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/packages/brew/cask.rs#L6977-L7013","documentation":"Before pruning a cask's artifacts, mise builds a claim map of every artifact target path from the receipts of all known casks (Homebrew-owned and mise-owned). If any target of the prune candidate is claimed by a receipt belonging to a different cask token, it bails — deleting would destroy the other cask's artifact. This is an ownership guard, not a filesystem error.","triggerScenarios":"Two casks' receipts record the same target path (shared bundle name, nested .app inside another cask's bundle, or a moved receipt), and one of them is being pruned/uninstalled via mise.","commonSituations":"Casks that install sub-apps into another cask's bundle directory; a cask reinstalled under a different token or renamed upstream; stale receipts from manually copied .app bundles.","solutions":["Prefer `brew uninstall --cask <token>` for the cask you want removed — Homebrew resolves shared artifacts itself","Inspect receipts to see who claims the path: look under the caskroom metadata/receipt dirs for both tokens before pruning","Remove the stale receipt of the cask you no longer have, so its false claim disappears, then retry the prune","Keep both casks if they genuinely share the artifact"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before pruning, confirm no other cask receipt claims the same targets:\nfn target_unclaimed(target: &std::path::Path, claims: &std::collections::HashMap<std::path::PathBuf, std::collections::BTreeSet<String>>, my_token: &str) -> bool {\n    claims\n        .get(target)\n        .map(|tokens| tokens.iter().all(|t| t == my_token))\n        .unwrap_or(true)\n}","typeGuard":null,"tryCatchPattern":"match validate_prune_targets(&candidate) {\n    Ok(()) => prune(),\n    Err(e) if e.to_string().contains(\"claimed by another cask\") => {\n        warn!(\"artifact shared with another cask; deferring to brew uninstall\");\n        Ok(())\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Use brew uninstall --cask for removals when Homebrew may also track the cask","Avoid manually copying .app bundles between cask-managed locations — receipts keep claiming the original path","Clean up stale receipts for casks you removed by hand before running prune"],"tags":["brew-cask","prune","ownership","receipts"],"backgroundTag":"ownership-conflict","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}