jdx/mise · warning

another history operation is running: {} since {} ({})

Error message

another history operation is running: {} since {} ({})

What it means

acquire_operation_lock takes an exclusive file lock for history operations. When the lock cannot be acquired before the deadline, mise reads the marker file left by the current holder and reports which operation is running, since when, and from which command, instead of deadlocking or corrupting shared state.

Source

Thrown at src/system/history/scope.rs:698

pub(crate) fn recovery_lock(store: &Store) -> Result<fslock::LockFile> {
    acquire_operation_lock(store, std::time::Duration::ZERO)
}

fn acquire_operation_lock(store: &Store, wait: std::time::Duration) -> Result<fslock::LockFile> {
    let state_dir = store.state_dir();
    let path = store::operation_lock_in(state_dir);
    // a running operation (the watcher applying incoming changes, say) is
    // usually over in moments: wait for it a bounded while before failing
    let deadline = std::time::Instant::now() + wait;
    let mut announced = false;
    let lock = loop {
        if let Some(lock) = LockFile::new(&path).try_lock()? {
            break lock;
        }
        let marker = store::read_marker_in(state_dir)?;
        if std::time::Instant::now() >= deadline {
            match marker {
                Some(marker) => bail!(
                    "another history operation is running: {} since {} ({})",
                    marker.kind.as_str(),
                    marker.started_at,
                    marker.command
                ),
                None => bail!("another history operation is running"),
            }
        }
        if !announced {
            announced = true;
            info!(
                "history: waiting for another history operation to finish{}",
                marker
                    .map(|marker| format!(": {} ({})", marker.kind.as_str(), marker.command))
                    .unwrap_or_default()
            );
        }
        std::thread::sleep(OPERATION_LOCK_POLL);

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Wait for the currently running history operation to finish, then retry
  2. Find and terminate the stuck mise process shown in the message
  3. If the holder is dead (stale lock), remove the lock/marker files in the state dir and retry
  4. Retry later; mise waits and logs progress before the deadline expires

Example fix

// stale lock from crashed process
rm "$(mise config get state_dir 2>/dev/null || echo ~/.local/state/mise)"/history/*.lock
// after: rerun the command once no mise history process is alive
Defensive patterns

Strategy: retry

Try / catch

match history_op() {
    Err(e) if e.to_string().contains("another history operation is running") => {
        wait_for_lock_release(); retry_with_backoff();
    }
    other => other,
}

Prevention

When it happens

Trigger: A concurrent history operation (install-time checkpoint, rollback, recovery, pull apply) holds the lock past the wait deadline; or a stale lock file with a marker from a crashed process.

Common situations: Two mise commands in different terminals/shells at once; a crashed mise left a lock and marker behind; a long rollback still running while you start another command.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/f8e0f7dcf09df1dd. Report an issue: GitHub.