jdx/mise · error

enrollment conflict in

Error message

enrollment conflict in {field}; reconcile repository metadata before syncing

What it means

When merging local and remote repository enrollment metadata, `choose` picks a value only when the conflict is trivially resolvable (local==remote, remote==base, or local==base). This error means local and remote both diverged from the base for the same field, so the tool refuses to pick a winner and asks the developer to reconcile the metadata manually.

Solutions

  1. Edit the enrollment metadata so local and remote agree on the conflicting field, then re-run sync
  2. Re-fetch/reset one side: overwrite local metadata with the remote version (or vice versa) if one edit is obsolete
  3. Bump the base by committing the reconciled manifest so future merges resolve cleanly
  4. Run the repo's sync/merge in a clean state (commit or discard local metadata changes before syncing)

Example fix

// before
// local: {format:1, variants:["linux"]}, remote: {format:1, variants:["macos"]} -> bail
// after
// manually unify then sync
local.variants = ["linux", "macos"];
merge(base, local, remote)?; // resolves
Defensive patterns

Strategy: validation

Validate before calling

// detect a three-way conflict before calling merge
fn conflicts<T: Clone + Eq>(base: &T, local: &T, remote: &T) -> bool {
  local != remote && local != base && remote != base
}

Try / catch

match enrollment.merge(base, remote) {
  Err(e) if e.to_string().contains("enrollment conflict") => {
    // prompt user to pick local vs remote, then re-merge
  }
  other => other,
}

Prevention

When it happens

Trigger: Calling `Enrollment::merge` (via `choose`) on a manifest where the same enrollment field (e.g. `format`, a variant list, or a permission) was edited independently on two machines/sync targets since the common base.

Common situations: Two devices syncing dotfile metadata both modified the enrollment manifest and then synced; a merge/rebase was skipped and conflicting manifests were committed; an out-of-band edit landed after a fetch.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/f4f9188d2239680a. Report an issue: GitHub.

Appendix: source

Thrown at src/system/history/manifest.rs:140

                self.permissions.insert(portable, *bits);
            }
        }
        self.remove_unenrolled_permissions();
        Ok(())
    }

    /// Merge enrollment by portable path rather than JSON line positions.
    /// Independent additions and policy edits commute; deletion versus a
    /// policy edit remains a conflict, never an implicit reenrollment.
    pub(crate) fn merge(base: &Self, local: &Self, remote: &Self) -> Result<Self> {
        use std::collections::BTreeMap;
        fn choose<T: Clone + Eq>(base: &T, local: &T, remote: &T, field: &str) -> Result<T> {
            if local == remote || remote == base {
                Ok(local.clone())
            } else if local == base {
                Ok(remote.clone())
            } else {
                bail!(
                    "enrollment conflict in {field}; reconcile repository metadata before syncing"
                )
            }
        }
        base.validate()?;
        local.validate()?;
        remote.validate()?;
        if local == remote || remote == base {
            return Ok(local.clone());
        }
        if local == base {
            return Ok(remote.clone());
        }
        let indexed = |manifest: &Self| {
            manifest
                .enrollment
                .iter()
                .map(|entry| (entry.path.clone(), entry.clone()))

View on GitHub (pinned to 533346cc37)