jdx/mise · error
origin changed while applying this setup; reconcile again…
Error message
origin changed while applying this setup; reconcile again before publication
What it means
Candidate::adopt applies a prepared history sync setup to the local history repository. Before committing, it re-reads the UPSTREAM_REF and compares it to the remote OID recorded when the candidate was computed. If the origin ref moved in the meantime, publishing would silently base history on a stale upstream, so the library refuses and demands recomputation.
Solutions
- Re-run the reconcile/candidate computation step to build a fresh Candidate against the current UPSTREAM_REF, then adopt again
- Fetch the latest origin state and rebase or merge local history onto it before re-adopting
- Ensure only one sync process runs at a time (lock or serialize sync operations) to avoid racing adopt
Example fix
// before: adopting a stale candidate
if let Ok(candidate) = compute_candidate(&repo) {
// ...long delay, origin moved...
candidate.adopt(&repo)?; // bails: origin changed
}
// after: recompute immediately before adopting
let candidate = compute_candidate(&repo)?; // reads current UPSTREAM_REF
candidate.adopt(&repo)?; Defensive patterns
Strategy: retry
Validate before calling
if repo.ref_oid(UPSTREAM_REF)? != candidate.expected_remote {
// stale candidate: recompute before adopting
candidate = compute_candidate(&repo)?;
} Try / catch
loop {
match candidate.adopt(&repo) {
Ok(()) => break,
Err(e) if e.to_string().contains("origin changed") => {
candidate = compute_candidate(&repo)?; // recompute and retry
}
Err(e) => return Err(e),
}
} Prevention
- Serialize history sync operations with a lock so only one process computes/adopts at a time
- Adopt immediately after computing a candidate; avoid long interactive delays in between
- Always fetch the latest origin state right before computing a candidate
When it happens
Trigger: Calling Candidate::adopt (directly or via the history sync apply path) after another process or a concurrent fetch/push updated the remote branch pointed to by UPSTREAM_REF between candidate creation and adoption; expected_local no longer matches because a boundary/save commit was inserted concurrently.
Common situations: Two machines or two terminals syncing the same history repository simultaneously; a background fetch landed new upstream commits while the user reviewed the pending setup; a slow interactive confirm step during onboarding that spans a remote push from elsewhere.
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
- local saved history changed while planning; reconcile again
- cannot onboard this setup repository: history is unavailable
- configuration disappeared from preview commit
- could not snapshot the local history repository for preview
- forced publication is not supported
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/13d770957ef28d1a.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/history/sync/graph.rs:90
{
remote.clone()
} else {
repo.commit_tree(tree, vec![local, remote], "merge origin dotfiles")?
}
}
};
Ok(Some(Candidate {
commit,
expected_local: self.local.clone(),
expected_remote: self.remote.clone(),
}))
}
}
impl Candidate {
pub(crate) fn adopt(&self, repo: &HistoryRepo) -> Result<()> {
if repo.ref_oid(UPSTREAM_REF)? != self.expected_remote {
bail!("origin changed while applying this setup; reconcile again before publication");
}
// Compare-and-swap also verifies no local boundary/save was inserted
// during application. The caller must recompute instead of dropping it.
repo.update_history_head(&self.commit, self.expected_local.as_deref())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fresh_adoption_keeps_remote_identity_and_rejects_a_changed_fetch() {
let temporary = tempfile::tempdir().unwrap();
let repo = HistoryRepo::open_or_init_in(temporary.path())
.unwrap()
.unwrap();
let incoming_tree = tree(&repo, b"incoming");View on GitHub (pinned to 533346cc37)