gitbutlerapp/gitbutler · error

' ' has no unresolved conflict

Error message

'{rela_path}' has no unresolved conflict

What it means

resolve_worktree_conflicts writes resolutions for a conflicted path by rewriting its index stages (Base, Ours, theirs). If removing/re-checking the stages does not change the sorted-entry count, the path has no unresolved conflict stages, so there is nothing to resolve and the call is rejected.

Solutions

  1. Only pass paths that appear as conflicted (multi-stage) in the current index; refresh the conflict list right before resolving
  2. Skip paths whose entries are already stage-0 instead of passing them to the API
  3. Check whether an earlier resolve_worktree_conflicts call already handled the path (avoid double resolution)
  4. Re-read the index after operations that may have resolved conflicts implicitly

Example fix

// before: resolving every recorded path blindly
resolve_worktree_conflicts(&wt, &mut index, all_conflicted_paths)?;
// after: filter to paths still multi-stage
let still_conflicted: Vec<_> = all_conflicted_paths.into_iter()
    .filter(|p| index.entries_with_path_by_stage(p).map(|e| e.len() > 1).unwrap_or(false))
    .collect();
resolve_worktree_conflicts(&wt, &mut index, still_conflicted)?;
Defensive patterns

Strategy: validation

Validate before calling

fn has_unresolved_conflict(index: &gix::index::File, rela_path: &str) -> bool {
    index.entries_with_path(rela_path)
        .map(|entries| entries.len() > 1 || entries.iter().any(|e| e.stage() != 0))
        .unwrap_or(false)
}

Try / catch

match resolve_worktree_conflicts(&wt, &mut index, &paths) {
    Err(e) if e.to_string().contains("has no unresolved conflict") => {
        // path already resolved — skip and continue
    }
    r => r,
}

Prevention

When it happens

Trigger: Calling resolve_worktree_conflicts with a rela_path whose index entries are already fully resolved (single stage-0 entry) or that has no conflict entries at all — e.g. resolving the same path twice, or a path that was never conflicted.

Common situations: Double-applying a resolution after an earlier call already cleaned the stages; passing a path from a stale conflict listing generated before a prior resolution or a `git checkout`/rebase updated the index; scripting bulk resolutions where some paths auto-merged.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/5643bd700d606387. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-workspace/src/worktree.rs:143

    let rela_paths: BTreeSet<_> = rela_paths.into_iter().collect();
    let mut index = repo.index_or_empty()?.into_owned_or_cloned();
    let (mut pipeline, index_for_filter) = repo.filter_pipeline(None)?;
    let mut path_check = gix::status::plumbing::SymlinkCheck::new(
        repo.workdir().context("non-bare repository")?.into(),
    );
    let mut num_sorted_entries = index.entries().len();

    for rela_path in &rela_paths {
        let rela_path = rela_path.as_bstr();
        let entries_before = num_sorted_entries;
        delete_entry_by_path_bounded_stages(
            &mut index,
            rela_path,
            &mut num_sorted_entries,
            &[Stage::Base, Stage::Ours, Stage::Theirs],
        );
        if entries_before == num_sorted_entries {
            anyhow::bail!("'{rela_path}' has no unresolved conflict");
        }
        // Stat before hashing so a write in between is caught by the stat mismatch.
        let md = match gix::index::fs::Metadata::from_path_no_follow(
            &path_check.verified_path_allow_nonexisting(rela_path)?,
        ) {
            Ok(md) => md,
            Err(err) if gix::fs::io_err::is_not_found(err.kind(), err.raw_os_error()) => {
                delete_entry_by_path_bounded_stages(
                    &mut index,
                    rela_path,
                    &mut num_sorted_entries,
                    &[Stage::Unconflicted],
                );
                continue;
            }
            Err(err) => return Err(err.into()),
        };
        let (id, kind, _) = pipeline

View on GitHub (pinned to 58e5313667)