jdx/mise · error
brew-cask: unresolved recovery for {} still preserves its or
Error message
brew-cask: unresolved recovery for {} still preserves its original at {} What it means
App 'flight' (linking into /Applications etc.) writes a recovery record before moving a target's original aside, and deletes the record once the transaction resolves. Before starting a new protected operation on the same target, mise scans the recovery directory; if a record for this target still exists and names a backup of the original file, mise refuses to proceed, because an earlier run crashed mid-transaction and the original is preserved at the backup path awaiting manual/automatic recovery.
Source
Thrown at src/system/packages/brew/cask/flight.rs:392
return Ok(());
};
for entry in entries.flatten() {
let path = entry.path();
if path
.extension()
.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)
}View on GitHub (pinned to afd2eddd3a)
Solutions
- Re-run the cask install — recover_flight_backups will attempt to restore the backup and clear the record on a healthy filesystem.
- Manually restore: move the backup shown in the message back to the target path, then delete the stale .recovery file under the mise state brew-cask/flight-recovery directory.
- If the backup and target both exist and you know the target is the newer good copy, remove the backup and its recovery record manually.
- Check parent-directory permissions so the automatic recovery rename can succeed on the next run.
Defensive patterns
Strategy: try-catch
Try / catch
// on this error, attempt recovery then retry once
match install_cask(token) {
Err(e) if e.to_string().contains("unresolved recovery") => {
// re-running triggers recover_flight_backups; manual fallback: restore
// the backup path printed in the message, then delete the .recovery file
retry(install_cask(token))
}
other => other,
} Prevention
- Don't kill mise (SIGKILL) during cask install/upgrade; let it finish or Ctrl-C cleanly.
- Re-run the install promptly after an interruption so automatic recovery can clear records.
- Avoid concurrent mise cask operations on the same app.
When it happens
Trigger: protect_with_elevation → ensure_no_unresolved_flight_recovery finds a *.recovery record whose FlightRecoveryRecord.target equals the target and whose backup field is set — i.e. a previous install/upgrade crashed or was killed between staging the backup and commit/recovery.
Common situations: A previous mise run interrupted by kill/power loss during a cask app-link transaction; recovery failed earlier (e.g. parent directory changed) leaving the record on disk; the same app being installed concurrently from two runs.
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
- brew-cask: unresolved recovery for newly created target {}
- brew-cask: unresolved recovery for {} still preserves its or
- brew-cask: unresolved recovery for newly created target {}
- interrupted file recovery needs attention; temporary recover
- brew-cask: refusing to restore flight target through a chang
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/f3f6f81efc8335ad.
Report an issue: GitHub.