jdx/mise · error
brew-cask: unresolved recovery for
Error message
brew-cask: unresolved recovery for {} still preserves its original at {} What it means
Before touching a target, ensure_no_unresolved_flight_recovery scans $STATE/brew-cask/flight-recovery/*.recovery records. Finding one whose target matches with a backup path set means a previous install/upgrade/uninstall was interrupted after moving the original file aside: that original is still preserved at the printed backup path and the transaction never committed. mise refuses the new operation rather than destroy the stranded original.
Solutions
- Re-run the same mise brew command — pending recovery records are processed first and the operation then proceeds
- Or restore manually: move the printed backup file back onto the target path, then delete the matching *.recovery record
- If the target is disposable, delete both the target and the recovery record, then retry
- Do not delete only the record while the backup file exists — that loses the original
Example fix
# before: stale recovery record $ mise brew install --cask foo # refuses, names backup path # after: manual resolve then retry $ mv "<backup-path-from-error>" "<target-path-from-error>" $ rm "<state>/brew-cask/flight-recovery/<hash>.recovery" $ mise brew install --cask foo
Defensive patterns
Strategy: validation
Validate before calling
// Before installing, check for pending recovery records naming your targets.
fn pending_recovery_for(target: &Path) -> Option<PathBuf> {
let dir = state_dir().join("brew-cask/flight-recovery");
std::fs::read_dir(dir).ok()?.flatten().map(|e| e.path())
.filter(|p| p.extension().is_some_and(|e| e == "recovery"))
.find(|p| std::fs::read_to_string(p).ok()
.and_then(|b| serde_json::from_str::<FlightRecoveryRecord>(&b).ok())
.is_some_and(|r| r.target == target && r.backup.is_some()))
} Try / catch
if let Err(e) = install_cask(&cask) {
if let Some(msg) = e.to_string().strip_prefix("brew-cask: unresolved recovery for") {
// surface the preserved-backup path to the user; do NOT auto-delete
}
} Prevention
- Let mise finish or roll back — don't kill it mid-cask-operation
- Resolve stale .recovery records (restore backup, delete record) before retrying
- Never delete a recovery record while its backup file still exists
When it happens
Trigger: A prior cask operation crashed, was Ctrl-C'd, or lost power mid-flight after the backup rename but before commit; the leftover .recovery JSON still names this target with a non-null backup field.
Common situations: Interrupted `mise` brew-cask installs or upgrades; CI jobs killed mid-run; laptop shutdown during install; anything that re-triggers the same target (reinstall/upgrade) afterward.
Related errors
- brew-cask: unresolved recovery for newly created target
- brew-cask: refusing to restore flight target through a…
- brew-cask: refusing to restore flight target through a…
- brew-cask: unresolved recovery for newly created target
- brew-cask: unresolved recovery for
AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22).
Data as JSON: /api/errors/1d0f7cf6cd7d9a05.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/packages/brew/cask.rs:3300
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(())
}
fn recover_flight_backups() -> Result<()> {
let root = flight_recovery_root();
recover_flight_backups_in(&root)
}View on GitHub (pinned to 6f52dcdf99)