{"record":{"id":"5643bd700d606387","repo":"gitbutlerapp/gitbutler","slug":"rela-path-has-no-unresolved-conflict","errorCode":null,"errorMessage":"'{rela_path}' has no unresolved conflict","messagePattern":"'(.+?)' has no unresolved conflict","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-workspace/src/worktree.rs","lineNumber":143,"sourceCode":"    let rela_paths: BTreeSet<_> = rela_paths.into_iter().collect();\n    let mut index = repo.index_or_empty()?.into_owned_or_cloned();\n    let (mut pipeline, index_for_filter) = repo.filter_pipeline(None)?;\n    let mut path_check = gix::status::plumbing::SymlinkCheck::new(\n        repo.workdir().context(\"non-bare repository\")?.into(),\n    );\n    let mut num_sorted_entries = index.entries().len();\n\n    for rela_path in &rela_paths {\n        let rela_path = rela_path.as_bstr();\n        let entries_before = num_sorted_entries;\n        delete_entry_by_path_bounded_stages(\n            &mut index,\n            rela_path,\n            &mut num_sorted_entries,\n            &[Stage::Base, Stage::Ours, Stage::Theirs],\n        );\n        if entries_before == num_sorted_entries {\n            anyhow::bail!(\"'{rela_path}' has no unresolved conflict\");\n        }\n        // Stat before hashing so a write in between is caught by the stat mismatch.\n        let md = match gix::index::fs::Metadata::from_path_no_follow(\n            &path_check.verified_path_allow_nonexisting(rela_path)?,\n        ) {\n            Ok(md) => md,\n            Err(err) if gix::fs::io_err::is_not_found(err.kind(), err.raw_os_error()) => {\n                delete_entry_by_path_bounded_stages(\n                    &mut index,\n                    rela_path,\n                    &mut num_sorted_entries,\n                    &[Stage::Unconflicted],\n                );\n                continue;\n            }\n            Err(err) => return Err(err.into()),\n        };\n        let (id, kind, _) = pipeline","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-workspace/src/worktree.rs#L125-L161","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Only pass paths that appear as conflicted (multi-stage) in the current index; refresh the conflict list right before resolving","Skip paths whose entries are already stage-0 instead of passing them to the API","Check whether an earlier resolve_worktree_conflicts call already handled the path (avoid double resolution)","Re-read the index after operations that may have resolved conflicts implicitly"],"exampleFix":"// before: resolving every recorded path blindly\nresolve_worktree_conflicts(&wt, &mut index, all_conflicted_paths)?;\n// after: filter to paths still multi-stage\nlet still_conflicted: Vec<_> = all_conflicted_paths.into_iter()\n    .filter(|p| index.entries_with_path_by_stage(p).map(|e| e.len() > 1).unwrap_or(false))\n    .collect();\nresolve_worktree_conflicts(&wt, &mut index, still_conflicted)?;","handlingStrategy":"validation","validationCode":"fn has_unresolved_conflict(index: &gix::index::File, rela_path: &str) -> bool {\n    index.entries_with_path(rela_path)\n        .map(|entries| entries.len() > 1 || entries.iter().any(|e| e.stage() != 0))\n        .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match resolve_worktree_conflicts(&wt, &mut index, &paths) {\n    Err(e) if e.to_string().contains(\"has no unresolved conflict\") => {\n        // path already resolved — skip and continue\n    }\n    r => r,\n}","preventionTips":["Refresh the conflict listing immediately before resolving","Skip paths already reduced to a single stage-0 entry","Don't call resolve twice for the same path in one run","Re-read the index after any operation that could auto-resolve conflicts"],"tags":["git","worktree","conflict-resolution","index"],"backgroundTag":"invalid-argument-value","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}