jdx/mise · error
setup history changed during adoption; retry pull
Error message
setup history changed during adoption; retry pull
What it means
During `mise dot sync` pull, apply_locked_with_scope re-reads the shadow-history heads after writing directories and verifies they still match the commit that was planned (application_head and the upstream commit). If another process (user edit, concurrent sync, GC) moved the local or remote head between planning and finalization, mise aborts with this error rather than applying a plan computed against stale history. It is a deliberate optimistic-concurrency check protecting against applying a pull onto history that has been rewritten underneath it.
Solutions
- Simply re-run `mise dot sync` (pull) — the message explicitly says retry; the new plan will be computed against the current heads.
- Ensure only one sync runs at a time (serialize syncs, or rely on mise's lock if available) so concurrent history writes stop racing.
- Avoid manually mutating the shadow history repo (git reset/prune/rebase) while a sync is active.
- If it recurs without concurrency, file a bug; it may indicate two code paths committing history without coordination.
Example fix
// before: two terminals racing # terminal A: mise dot sync & # terminal B: mise dot sync # -> setup history changed during adoption; retry pull // after # terminal A: mise dot sync # terminal B: wait for A, then: mise dot sync
Defensive patterns
Strategy: retry
Validate before calling
// before pulling, check no other sync is running pgrep -f 'mise dot sync' && echo 'another sync in progress — wait' || mise dot sync
Try / catch
// retry with backoff on the specific message
for attempt in 1..=3 {
match run_sync() {
Ok(()) => break,
Err(e) if e.contains("setup history changed during adoption") => sleep(backoff(attempt)),
Err(e) => return Err(e),
}
} Prevention
- Run only one `mise dot sync` at a time.
- Never hand-edit or git-mutate the shadow history repo during a sync.
- Schedule syncs away from dotfile-watching daemons that commit history.
When it happens
Trigger: Another `mise dot sync` (pull or push) or history-writing process commits to the shadow repo while a pull is being applied, so Heads::read at apply.rs:676 returns heads.local != application_head or heads.remote != status.upstream_commit. Also caused by external mutation of the shadow history store (manual git commands in the history repo, history GC/pruning) mid-pull.
Common situations: Running `mise dot sync` in two terminals at once; an editor/daemon that touches dotfiles while a slow pull is in flight; running history maintenance (prune/rebase of the history repo) concurrently with a pull.
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
- changed after permission planning; retry pull
- changed while the changes were being applied; nothing more…
- origin changed while planning; reconcile again
- origin changed while preparing publication; reconcile again
- another setup sync or pull is running; retry shortly
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/649f2480150ac146.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/history/sync/apply.rs:676
if fresh_adoption || inventory_tree.is_some() {
// Verify the whole written batch again before advancing Git. A
// concurrent edit must not become an apparently applied setup.
for step in &ready {
if live_object(repo, &step.path)? != step.pending.object
|| live_permissions(&step.path)? != step.desired_mode
{
bail!(
"{} changed during setup adoption; retry pull",
display_path(&step.path)
);
}
}
for directory in &directories {
directory.verify_written()?;
}
let heads = super::graph::Heads::read(repo)?;
if heads.local != application_head || heads.remote != status.upstream_commit {
bail!("setup history changed during adoption; retry pull");
}
let remote = heads.remote.as_deref().unwrap();
audit_incoming_history(repo, remote)?;
let tree = inventory_tree
.clone()
.unwrap_or(repo.output_tree_of(remote)?);
let candidate = heads
.candidate(repo, &tree)?
.ok_or_else(|| eyre::eyre!("setup branch disappeared during adoption"))?;
candidate.adopt(repo)?;
}
Ok(())
})();
if let Err(error) = &result {
let mut recovery_errors = vec![];
for step in ready
.iter()
.rev()View on GitHub (pinned to 533346cc37)