{"record":{"id":"5281b3162d7a5656","repo":"jdx/mise","slug":"artifact-target-is-now-claimed-by-another-cask-5281b3","errorCode":null,"errorMessage":"artifact target is now claimed by another cask: {}","messagePattern":"artifact target is now claimed by another cask: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/state.rs","lineNumber":1021,"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\npub(super) fn 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":1003,"sourceCodeEnd":1039,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/packages/brew/cask/state.rs#L1003-L1039","documentation":"validate_cask_prune_claims re-checks, immediately before pruning a mise-managed cask, that none of its recorded artifact targets have been claimed by a different cask since the plan was made. The claims map maps target paths to owning tokens; if some other token now owns a target, pruning would destroy another cask's artifacts, so mise bails naming the path.","triggerScenarios":"apply_cask_prune_plan_in → validate_cask_prune_claims finds claims.get(&target.path) containing any token other than the candidate's token for one of receipt.targets — a TOCTOU revalidation between plan and apply.","commonSituations":"Two casks legitimately claiming the same binary/app path after a cask change; another mise or Homebrew operation installed an overlapping cask between planning and pruning; stale receipts listing shared targets.","solutions":["Re-run the prune operation to regenerate a fresh plan reflecting current ownership","Identify which cask now claims the path (mise cask state/list or Homebrew metadata) and resolve the overlap (uninstall or update one of them)","Refresh the cask's ownership receipt (reinstall via mise) so its targets match current claims"],"exampleFix":"// before: prune planned while another cask later claimed /usr/local/bin/tool\nmise cask prune <token>            # fails\n// after: refresh state and re-plan\nmise reinstall <token> && mise cask prune <token>","handlingStrategy":"retry","validationCode":"// after a claim conflict, re-plan instead of forcing\n// mise revalidates claims itself; the caller should simply re-run planning\nretry_prune_with_fresh_plan(token);","typeGuard":null,"tryCatchPattern":"if let Err(e) = prune(plan) {\n    if e.to_string().contains(\"claimed by another cask\") {\n        let plan = build_fresh_plan(token)?;\n        return prune(plan);\n    }\n    return Err(e);\n}","preventionTips":["Apply prune plans immediately after building them","Avoid concurrent cask installs/prunes that can share target paths","Refresh ownership receipts (reinstall) after cask changes"],"tags":["cask","ownership","prune","conflict","toctou"],"backgroundTag":"invalid-state-transition","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}