jdx/mise · error

local saved history changed while planning; reconcile again

Error message

local saved history changed while planning; reconcile again

What it means

Before publishing saved history, mise re-reads the shadow repository's heads and compares the local head against the value captured when the plan was built (`expected_local`). If the local branch moved between planning and publication — another sync, pull, or local write happened concurrently — mise aborts and asks the user to reconcile again, preventing publication based on stale assumptions.

Source

Thrown at src/system/history/sync/publish.rs:20

use eyre::{Result, bail};
use std::collections::BTreeSet;

use super::graph::Heads;
use super::network::{PushOutcome, Remote};
use crate::system::history::shadow::HistoryRepo;

/// Reconciliation and complete live application must precede publication.
/// A merge that still changes this machine's saved tree needs another pull.
pub(crate) fn build(
    repo: &HistoryRepo,
    upstream: Option<&str>,
    expected_local: Option<&str>,
    accepted: &BTreeSet<String>,
) -> Result<Option<String>> {
    let heads = Heads::read(repo)?;
    if heads.local.as_deref() != expected_local {
        bail!("local saved history changed while planning; reconcile again");
    }
    if heads.remote.as_deref() != upstream {
        bail!("origin changed while preparing publication; reconcile again");
    }
    let Some(local) = &heads.local else {
        return Ok(None);
    };
    if heads.remote.as_ref() == Some(local) {
        return Ok(None);
    }
    let tree = repo.output_tree_of(local)?;
    if let Some(remote) = &heads.remote
        && heads.base.as_ref() != Some(remote)
    {
        let (mut merged, mut conflicts) = repo.merge_tree(local, remote)?;
        // Enrollment is a keyed inventory, not arbitrary JSON text. Git's
        // line merge can conflict on independent additions or combine policy
        // changes into invalid metadata. Validate its structured merge first.

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Re-run the reconcile/plan step (`mise bootstrap dotfiles pull` or equivalent) so `expected_local` is refreshed, then publish again.
  2. Ensure no other mise sync process is running concurrently (single writer).
  3. If it recurs, serialize sync operations (script them sequentially) rather than invoking pull and publish in parallel.

Example fix

// before: prepare and publish interleaved with another process
mise bootstrap dotfiles publish &
mise bootstrap dotfiles pull            # moves local head -> stale expected_local
// after
mise bootstrap dotfiles pull && mise bootstrap dotfiles publish   # sequential, no concurrency
Defensive patterns

Strategy: retry

Validate before calling

const heads = await readHeads();
if (heads.local !== expectedLocal) {
  console.warn("local head moved; rerun reconcile before publishing");
}

Try / catch

try {
  await publish();
} catch (e) {
  if (String(e.message).includes("changed while planning")) {
    await reconcile();
    await publish();   // retry once with a fresh plan
  } else throw e;
}

Prevention

When it happens

Trigger: In publish `build`, `Heads::read(repo).local != expected_local`. Triggered by any concurrent modification of the saved-history branch between the plan/prepare step and the publish step: another mise process pulling or committing, or a changed accepted set applied locally in the meantime.

Common situations: Two terminals running mise bootstrap dotfiles pull/publish at once; a background sync job committed saved history while the user was publishing; the user ran a reconcile between the prepare and publish commands.

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@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/654510ff8117ac5d. Report an issue: GitHub.