jdx/mise · error · eyre::Report

ownership receipt has changed

Error message

ownership receipt has changed

What it means

Thrown while pruning a mise-managed Homebrew cask: apply_cask_prune_plan re-reads the cask's ownership receipt (.mise-cask.toml inside the Caskroom version directory) and compares it to the snapshot taken when the prune plan was built. A mismatch means the cask's ownership changed between planning and execution, so pruning is refused rather than risk deleting artifacts that now belong to a different install.

Source

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

            .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");
    }
    let records = receipt
        .targets
        .iter()
        .map(|record| (record.path.clone(), record))
        .collect::<BTreeMap<_, _>>();
    let expected = receipt
        .apps
        .iter()
        .chain(&receipt.binaries)
        .chain(&receipt.fonts)
        .chain(&receipt.completions)
        .cloned()
        .collect::<BTreeSet<_>>();
    if expected.is_empty()

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Re-run the prune so it replans from the receipts currently on disk (plans are validated against live state, stale plans are expected to be discarded)
  2. Make sure no other mise brew install/upgrade/uninstall or Homebrew command is running against the same prefix while pruning
  3. Inspect Caskroom/<token>/<version>/.mise-cask.toml — if it is corrupt or foreign, reinstall the cask to regenerate a schema-3 receipt

Example fix

// before: pruning from a plan built earlier, while another mise process ran
let plan = cask_prune_plan(...)?;
// ... other mise brew install runs here, rewriting receipts ...
apply_cask_prune_plan(&plan, false)?; // "ownership receipt has changed"

// after: plan and apply in one session, no concurrent brew/mise operations
let plan = cask_prune_plan(...)?;
apply_cask_prune_plan(&plan, false)?;
Defensive patterns

Strategy: validation

Validate before calling

// Re-read the receipt immediately before applying a prune plan and drop
// candidates whose receipt no longer matches the plan snapshot.
fn still_valid(candidate: &CaskPruneCandidate) -> bool {
    read_receipt(&candidate.version_dir)
        .ok()
        .flatten()
        .is_some_and(|current| current == candidate.receipt)
}
let plan_remove: Vec<_> = plan.remove.into_iter().filter(still_valid).collect();

Type guard

fn receipt_unchanged(c: &CaskPruneCandidate) -> bool {
    matches!(read_receipt(&c.version_dir), Ok(Some(ref r)) if r == &c.receipt)
}

Try / catch

if let Err(reason) = validate_cask_prune_candidate(&candidate) {
    // do not abort the whole prune; skip this cask and continue,
    // surfacing the reason to the user (same pattern as apply_cask_prune_plan)
    warn!("brew-cask:{}: skipped: {reason:#}", candidate.token);
    continue;
}

Prevention

When it happens

Trigger: apply_cask_prune_plan -> validate_cask_prune_candidate: read_receipt(version_dir) returns a receipt not equal (PartialEq) to candidate.receipt. Happens when a concurrent mise brew install/upgrade rewrites the receipt after prune_plan ran, when Homebrew takes over the cask and metadata changes, or when .mise-cask.toml is edited/deleted between plan and apply.

Common situations: Two mise (or mise + brew) processes racing on the same Caskroom; running prune from a stale plan file; an interrupted earlier transaction leaving a half-written receipt; hand-editing receipts.

Related errors


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