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
- Re-run the operation after other mise history processes finish (the failure is transient concurrency).
- Inspect the history repo for unexpected commits between the two tree states (`git log history-ref`) and reconcile or reset unwanted changes.
- 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
- Avoid running multiple concurrent mise history-mutating commands.
- Never manually commit to the history branch.
- Serialize history operations with a lock when scripting mise.
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
- origin changed while preparing publication; reconcile again
- another history operation is running
- another history operation is running
- appeared after was protected; nothing more was changed
- changed after it was protected; nothing more was changed
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)