jdx/mise · error

brew-cask: unresolved recovery for newly created target {}

Error message

brew-cask: unresolved recovery for newly created target {}

What it means

Companion to the backup-case recovery error: when a flight transaction created a target that didn't exist before, the recovery record has no backup. If such a record still exists for this target, a previous run crashed after creating the target but before committing, and mise refuses to touch the target to avoid destroying a possibly-user-created file. The stale target must be resolved (recovered or removed) first.

Source

Thrown at src/system/packages/brew/cask/flight.rs:398

            .is_none_or(|extension| extension != "recovery")
        {
            continue;
        }
        let Ok(body) = file::read_to_string(&path) else {
            continue;
        };
        let Ok(record) = serde_json::from_str::<FlightRecoveryRecord>(&body) else {
            continue;
        };
        if record.target == target {
            if let Some(backup) = record.backup {
                bail!(
                    "brew-cask: unresolved recovery for {} still preserves its original at {}",
                    target.display(),
                    backup.display()
                );
            }
            bail!(
                "brew-cask: unresolved recovery for newly created target {}",
                target.display()
            );
        }
    }
    Ok(())
}

pub(super) fn recover_flight_backups() -> Result<()> {
    let root = flight_recovery_root();
    recover_flight_backups_in(&root)
}

pub(super) fn recover_flight_backups_in(root: &Path) -> Result<()> {
    let Ok(entries) = std::fs::read_dir(root) else {
        return Ok(());
    };
    for entry in entries {

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Re-run the install so recover_flight_backups can evaluate the target (via receipt claim) and clear the record.
  2. If the target is a leftover from the crashed run and you don't need it, delete it manually, then remove the corresponding absent-*.recovery file in mise's brew-cask/flight-recovery state dir.
  3. If the target is user data you created, keep it and remove the stale recovery record after confirming no install is in progress.
  4. Avoid running two mise cask operations on the same app simultaneously.
Defensive patterns

Strategy: try-catch

Try / catch

// resolve the stale 'newly created' target before reinstalling
match install_cask(token) {
    Err(e) if e.to_string().contains("unresolved recovery for newly created target") => {
        // inspect the target path in the message; delete if it is a crash leftover,
        // then remove the absent-*.recovery record and retry
        retry(install_cask(token))
    }
    other => other,
}

Prevention

When it happens

Trigger: protect_with_elevation → ensure_no_unresolved_flight_recovery finds a *.recovery record whose target matches and whose backup field is None (an 'absent-*' record) — a prior run died between creating the new target and finishing the transaction.

Common situations: Interrupted first-time install of a cask app (no previous version to back up); a crashed run left the newly linked app in place; concurrent mise runs racing on the same app target.

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/c87cbfe453d099e4. Report an issue: GitHub.