jdx/mise · error

history changed during the operation; cannot replace its…

Error message

history changed during the operation; cannot replace its before state

What it means

`protect_manual_preimage` in the history checkpoint module validates that the history repo's HEAD output tree has not moved between reading the checkpoint's `before` tree and replacing it. If the history branch ref changed concurrently (another mise process committed, or a gc/rebase ran), replacing the preimage would corrupt history, so it aborts.

Solutions

  1. Re-run the operation after other mise history processes finish (the failure is transient concurrency).
  2. Inspect the history repo for unexpected commits between the two tree states (`git log history-ref`) and reconcile or reset unwanted changes.
  3. Avoid manually editing the history branch; let mise manage it exclusively.
Defensive patterns

Strategy: retry

Validate before calling

let tree = repo.output_tree_of(before)?;
let head = repo.ref_oid(HistoryRepo::HISTORY_REF)?;
if repo.output_tree_of(&head?)? != tree { /* history moved: re-read and retry */ }

Try / catch

// retry with backoff when history moved
match protect_manual_preimage(...) {
    Err(e) if e.to_string().contains("history changed") => retry_after_delay(),
    other => other,
}

Prevention

When it happens

Trigger: Calling `protect_manual_preimage` while another process updates the history branch between `output_tree_of(before)` and the `ref_oid(HISTORY_REF)` comparison, causing the two tree OIDs to differ.

Common situations: Two mise sessions running history-tracking commands concurrently; a background checkpoint/garbage-collect advanced the history ref; a user manually committed to the history repo.

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/56a574c215ec311d. Report an issue: GitHub.

Appendix: source

Thrown at src/system/history/checkpoint/preimages.rs:24

    pub(crate) fn protect_manual_preimage(
        &self,
        before: &str,
        tracked: &TrackedSet,
        path: &Path,
        prior: &PathSnapshot,
        previous: &[JournalEntry],
    ) -> Result<Option<Box<Entry>>> {
        let _lock = self.lock()?;
        let repo = self
            .repo
            .as_ref()
            .ok_or_else(|| eyre::eyre!("Git is unavailable"))?;
        let mut checkpoint = repo.read_meta(before)?;
        let tree = repo.output_tree_of(before)?;
        let head = repo
            .ref_oid(HistoryRepo::HISTORY_REF)?
            .ok_or_else(|| eyre::eyre!("history branch disappeared"))?;
        eyre::ensure!(
            repo.output_tree_of(&head)? == tree,
            "history changed during the operation; cannot replace its before state"
        );
        let mut promotion = Promotion {
            store: self,
            repo,
            tracked,
            previous,
            overlays: BTreeMap::new(),
            modes: checkpoint.tree.modes.clone(),
        };
        promotion.snapshot(path, prior, &tree)?;
        if promotion.overlays.is_empty() && promotion.modes == checkpoint.tree.modes {
            return Ok(None);
        }
        let overlays: Vec<_> = promotion
            .overlays
            .into_iter()

View on GitHub (pinned to 533346cc37)