jdx/mise · warning · eyre::Report

receipt target inventory is incomplete or duplicated

Error message

receipt target inventory is incomplete or duplicated

What it means

The receipt's classified artifact lists (apps, binaries, fonts, completions) are cross-checked against receipt.targets: the expected set must be non-empty, the deduplicated target-record map must equal receipt.targets in size (no duplicate paths), the record count must equal the expected set size (every classified path has exactly one record), and every metadata_only_apps entry must appear in apps. Any inconsistency means the receipt is internally incomplete or duplicated, so the prune is refused.

Solutions

  1. Reinstall the cask with the current mise to regenerate a self-consistent receipt, then prune.
  2. Inspect `<Caskroom>/<token>/<version>/.mise-cask.toml` for duplicate or missing target entries if manual repair is acceptable.
  3. If it reproduces on a fresh install, file a mise bug with the receipt contents.
Defensive patterns

Strategy: validation

Validate before calling

// Mirror the receipt cross-check before planning a prune
use std::collections::{BTreeMap, BTreeSet};
fn inventory_consistent(r: &CaskReceipt) -> bool {
    let expected: BTreeSet<_> = r.apps.iter().chain(&r.binaries).chain(&r.fonts).chain(&r.completions).cloned().collect();
    let records: BTreeMap<_, _> = r.targets.iter().map(|t| (t.path.clone(), t)).collect();
    !expected.is_empty()
        && records.len() == r.targets.len()
        && records.len() == expected.len()
        && r.metadata_only_apps.iter().all(|p| r.apps.contains(p))
}

Try / catch

Wrap validate_cask_prune_candidate in `if let Err(reason)` and record a skip; treat any inconsistency as 'receipt unusable — reinstall to regenerate'.

Prevention

When it happens

Trigger: A receipt where the same path appears twice in targets; a classified path (in apps/binaries/fonts/completions) with no corresponding target record; an empty artifact set; a metadata_only_apps path not listed in apps. Produced by receipt-writer bugs or a corrupted `.mise-cask.toml`.

Common situations: Receipts written by an older/newer mise with a different layout; truncated or corrupt TOML after a crash mid-write; hand-edited receipts.

Related errors


AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22). Data as JSON: /api/errors/bcce9541b7eb9bb6. Report an issue: GitHub.

Appendix: source

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

        .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()
        || records.len() != receipt.targets.len()
        || records.len() != expected.len()
        || receipt
            .metadata_only_apps
            .iter()
            .any(|path| !receipt.apps.contains(path))
    {
        bail!("receipt target inventory is incomplete or duplicated");
    }
    if records.keys().any(|path| !expected.contains(path)) {
        bail!("receipt target inventory contains an unclassified path");
    }

    for path in &receipt.apps {
        let record = records
            .get(path)
            .ok_or_else(|| eyre!("missing app target record"))?;
        if record.fingerprint.kind != CaskTargetKind::Directory
            || !allowed_appdir_roots()?
                .iter()
                .any(|root| path_is_below(path, root))
            || !path.file_name().is_some_and(|name| {
                staged_app_matches_target(record, &candidate.version_dir.join(name))
            })
        {
            bail!(

View on GitHub (pinned to 6f52dcdf99)