jdx/mise · error

{}

Error message

{}

What it means

When `mise lock` finishes (or rolls back), it restores snapshotted lockfile contents; any per-file restore failures are collected and joined into a single error via `bail!("{}", errors.join("\n"))`. This error surfaces one or more underlying restore failures (I/O, permissions, missing files) that prevented returning the filesystem to its pre-command state.

Solutions

  1. Read the joined messages to identify which files failed to restore
  2. Fix permissions on the listed lockfiles (chmod/chown) and re-run the command
  3. Check disk space and that the lockfiles still exist; restore from git if contents were lost (`git checkout -- mise.lock`)

Example fix

// before
chmod 444 mise.lock
// after
chmod 644 mise.lock
mise lock
Defensive patterns

Strategy: try-catch

Validate before calling

// bash: ensure lockfiles are writable before locking
for f in mise.lock; do [[ -w "$f" ]] || chmod u+w "$f"; done

Try / catch

// bash
if ! mise lock; then git checkout -- mise.lock; mise lock; fi

Prevention

When it happens

Trigger: `restore_lockfile_snapshots` fails to write back original lockfile content for one or more paths — e.g. read-only filesystem, file deleted by another process, permission change — during normal completion or rollback (as in the rollback tests referenced).

Common situations: Lockfile made read-only by git or filesystem permissions; disk full; another process deleting the lockfile mid-run; rollback after a mid-command failure.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/679c0868a834e4a2. Report an issue: GitHub.

Appendix: source

Thrown at src/cli/lock.rs:294

    let mut errors = Vec::new();
    for rollback in rollbacks.into_iter().rev() {
        let result = match rollback.replacement {
            Some(replacement) => replacement.commit(),
            None => match fs::remove_file(&rollback.path) {
                Ok(()) => Ok(()),
                Err(err) if err.kind() == ErrorKind::NotFound => Ok(()),
                Err(err) => Err(err.into()),
            },
        };
        if let Err(err) = result {
            errors.push(format!("{}: {err:?}", display_path(&rollback.path)));
        }
    }
    lockfile::invalidate_caches();
    if errors.is_empty() {
        Ok(())
    } else {
        bail!("{}", errors.join("\n"))
    }
}

#[derive(Debug, Eq, PartialEq)]
enum LockTaskStatus {
    Updated,
    Unresolved,
    Failed,
    ProvenanceFailed,
}

fn classify_lock_result(
    resolution_error: Option<String>,
    error_is_fatal: bool,
    applied: bool,
) -> (LockTaskStatus, Option<String>) {
    if let Some(error) = resolution_error.filter(|_| error_is_fatal) {
        (LockTaskStatus::Failed, Some(error))

View on GitHub (pinned to 533346cc37)