jdx/mise · error

artifact target is now claimed by another cask: {}

Error message

artifact target is now claimed by another cask: {}

What it means

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.

Source

Thrown at src/system/packages/brew/cask/state.rs:1021

            let version = version?;
            if !version.file_type()?.is_dir()
                || version.file_name().to_string_lossy().starts_with('.')
            {
                continue;
            }
            if let Some(receipt) = read_receipt(&version.path())? {
                for target in receipt.targets {
                    claims.entry(target.path).or_default().insert(token.clone());
                }
            }
        }
    }
    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(())
}

pub(super) 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");

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Re-run the prune operation to regenerate a fresh plan reflecting current ownership
  2. Identify which cask now claims the path (mise cask state/list or Homebrew metadata) and resolve the overlap (uninstall or update one of them)
  3. Refresh the cask's ownership receipt (reinstall via mise) so its targets match current claims

Example fix

// before: prune planned while another cask later claimed /usr/local/bin/tool
mise cask prune <token>            # fails
// after: refresh state and re-plan
mise reinstall <token> && mise cask prune <token>
Defensive patterns

Strategy: retry

Validate before calling

// after a claim conflict, re-plan instead of forcing
// mise revalidates claims itself; the caller should simply re-run planning
retry_prune_with_fresh_plan(token);

Try / catch

if let Err(e) = prune(plan) {
    if e.to_string().contains("claimed by another cask") {
        let plan = build_fresh_plan(token)?;
        return prune(plan);
    }
    return Err(e);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/5281b3162d7a5656. Report an issue: GitHub.