jdx/mise · error

interrupted file recovery needs attention; temporary…

Error message

interrupted file recovery needs attention; temporary recovery data was retained. {}. Run `mise dot recover` to retry, or inspect the files and use `recover <operation> --keep-current` to explicitly accept their current contents

What it means

recover_entries attempts to restore each path from an interrupted write operation. When any individual path recovery fails, it collects the per-path failures and bails, retaining the temporary recovery data so nothing is lost, and tells the user to run `mise dot recover` or accept current contents with `recover <operation> --keep-current`.

Solutions

  1. Run `mise dot recover` to retry recovery of the retained data.
  2. For each listed failing path, compare the file against the intended content; if the current content is acceptable, run `mise dot recover <operation> --keep-current` to accept it explicitly.
  3. Inspect the recovery/temp data referenced by the error for files that need manual copying back into place.
  4. If files were changed intentionally, resolve path-by-path: restore expected content manually then re-run recover, or keep-current per operation.

Example fix

// before: fails with retained temp data
$ mise dot status   # shows unfinished recovery
// after
$ mise dot recover                       # retry
$ mise dot recover <operation> --keep-current   # accept current contents
Defensive patterns

Strategy: try-catch

Validate before calling

export function hasUnfinishedRecovery(statusOutput: string): boolean {
  return /unfinished|pending recovery/i.test(statusOutput);
}
// run `mise dot status` before writes that could be interrupted

Try / catch

try {
  await dotRecover();
} catch (e) {
  if (/interrupted file recovery needs attention/.test(String(e))) {
    // temp data retained; resolve per listed path
    for (const p of listedPaths(String(e))) reviewOrKeepCurrent(p);
    await dotRecover();
  } else throw e;
}

Prevention

When it happens

Trigger: recover_entries (via recover or recover_unfinished) iterates pending recovery entries and one or more recover_path calls return Err — e.g. the live file changed since the operation, the write completion was never recorded, a directory's contents could not be verified, or the snapshot validation failed.

Common situations: A mise process was killed mid dotfile write (power loss, Ctrl-C, OOM) leaving an unfinished recovery journal; on retry, the user or another tool edited the affected files so the recorded snapshots no longer match; the recovery store is partially corrupt.

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@533346cc37 (2026-09-17). Data as JSON: /api/errors/7bd17f69ba2acf9e. Report an issue: GitHub.

Appendix: source

Thrown at src/system/history/recovery.rs:56

            continue;
        }
        let after = journal.iter().rev().find_map(|entry| match entry {
            JournalEntry::Committed {
                seq: recorded,
                after,
            } if *recorded as usize == seq => Some(after),
            _ => None,
        });
        if unfinished_only && after.is_some() {
            continue;
        }
        if let Err(error) = recover_path(state_dir, path, prior, after) {
            blocked.push(path);
            failures.push(format!("{}: {error:#}", crate::file::display_path(path)));
        }
    }
    if !failures.is_empty() {
        bail!(
            "interrupted file recovery needs attention; temporary recovery data was retained. {}. Run `mise dot recover` to retry, or inspect the files and use `recover <operation> --keep-current` to explicitly accept their current contents",
            failures.join("; ")
        );
    }
    Ok(())
}

fn recover_path(
    state_dir: &Path,
    path: &Path,
    prior: &PathSnapshot,
    after: Option<&PathState>,
) -> Result<()> {
    validate_destination(path)?;
    let comparison = tempfile::tempdir()?;
    let depth = if matches!(prior, PathSnapshot::Directory { .. }) {
        Capture::Shallow
    } else {

View on GitHub (pinned to 533346cc37)