jdx/mise · error
choose only one resolution for
Error message
choose only one resolution for {} What it means
During a dotfile sync apply, conflicts can be resolved with a blanket choice (take remote or keep local) plus per-path choices. If the same path appears in both the take-remote and keep-local sets, the resolution is contradictory and mise fails with this error naming the file. You must pick exactly one resolution per path.
Solutions
- Remove the duplicated path from one of the two resolution groups.
- Decide per path: either take the remote version or keep the local one, not both.
- If the lists are generated, deduplicate and validate that the two sets are disjoint before invoking.
- Re-run the apply after correcting the resolution sets.
Example fix
// before mise dotfiles pull --take-remote ~/.gitconfig --keep-local ~/.gitconfig Error: choose only one resolution for ~/.gitconfig // after mise dotfiles pull --take-remote ~/.gitconfig
Defensive patterns
Strategy: validation
Validate before calling
let dupes: HashSet<_> = take_remote.intersection(&keep_local).collect();
if !dupes.is_empty() {
eprintln!("conflicting resolutions for: {:?}", dupes);
} Type guard
fn resolutions_are_disjoint(take_remote: &HashSet<PathBuf>, keep_local: &HashSet<PathBuf>) -> bool {
take_remote.is_disjoint(keep_local)
} Prevention
- Build the take-remote and keep-local path sets from one source of truth.
- Deduplicate path lists and assert disjointness before invoking apply.
- Decide a single resolution policy per path in automation.
When it happens
Trigger: Invoking apply/pull where conflict-resolution flags/options put the same path into both `take_remote` and `keep_local` — e.g. passing the same path to both a '--theirs' style list and a '--ours' style list in one command.
Common situations: Command lines like `mise dotfiles pull --take ~/.gitconfig --keep ~/.gitconfig`; generated commands where duplicate paths land in both option groups; copy-paste errors in long path lists.
Related errors
- cannot synchronize
- history is disabled (history.enabled = false)
- partial pulls are not supported: apply the complete setup…
- sync paused: resolve all
- : tracked in place; pass `--mode copy` after `mise…
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/af2a777318023f56.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/history/sync/apply.rs:199
};
blanket.extend(blanket_chosen.iter().cloned());
}
// Store choices without publishing or applying any part of the setup.
let mut sync_state = state::load(repo)?;
let shared = super::share::current(repo, tracked)?.objects();
let encrypted = super::files::encrypted_paths(repo, status.upstream_commit.as_deref())?;
for conflict in &status.conflicts {
let Some(local) = roots
.locate(&conflict.branch_path)
.path()
.map(Path::to_path_buf)
else {
continue;
};
if take_remote.contains(&local) || keep_local.contains(&local) {
if take_remote.contains(&local) && keep_local.contains(&local) {
bail!("choose only one resolution for {}", display_path(&local));
}
// A blanket choice cannot speak for a path whose live side is not
// a file or symlink, and failing here would discard every other
// decision this pass makes. Hold that one path and keep going; a
// path named on the command line still reports the failure. The
// check belongs on this read, so a path that changed since the
// blanket choice was made is held too rather than aborting it.
// Only the path's own fault is held: a repository failure on an
// otherwise sound file is nobody's to fix by hand, so it stops
// the pass with its own error.
let live = match live_object(repo, &local) {
Ok(live) => live,
Err(err)
if blanket_chosen.contains(&local)
&& err.downcast_ref::<UnusableLive>().is_some() =>
{
blanket_held.push(err.to_string());
take_remote.remove(&local);View on GitHub (pinned to 533346cc37)