gitbutlerapp/gitbutler · error
Encountered conflict when merging tree {tree_to_merge}{detai
Error message
Encountered conflict when merging tree {tree_to_merge}{details} What it means
During octopus/merge re-merging, `but_rebase::merge` three-way-merges each parent tree of a merge commit into the accumulating result using fail-fast merge options; any unresolved conflict aborts with this error. The error is wrapped with a `ConflictErrorContext` carrying the conflicted paths (`c.ours.location()`), so callers can downcast to recover the exact files. By design there is no conflict hiding: workspace-tip merges must apply cleanly.
Source
Thrown at crates/but-rebase/src/merge.rs:72
Ok(but_core::Commit::from_id(commit_id.attach(repo))?
.tree_id_or_kind(TreeKind::Theirs)?
.detach())
})
.collect::<Result<Vec<_>, _>>()?
.into_iter();
let mut ours = trees_to_merge.next().expect("two or more trees");
let (merge_options, unresolved) = repo.merge_options_fail_fast()?;
let mut successfully_merged = vec![ours];
for tree_to_merge in trees_to_merge {
let mut merge = repo.merge_trees(
merge_base,
ours,
tree_to_merge,
repo.default_merge_labels(),
merge_options.clone(),
)?;
if merge.has_unresolved_conflicts(unresolved) {
return Err(anyhow!(
"Encountered conflict when merging tree {tree_to_merge}{details}",
details = merge_conflict_details(&successfully_merged)
)
.context(ConflictErrorContext {
paths: merge
.conflicts
.iter()
.map(|c| c.ours.location().to_owned())
.collect(),
}));
}
successfully_merged.push(tree_to_merge);
ours = merge.tree.write()?.detach();
}
target_merge_commit.tree = ours;
if but_core::commit::Headers::try_from_commit(&target_merge_commit).is_none() {
let headers = but_core::commit::Headers::from_config(&repo.config_snapshot());
headers.set_in_commit(&mut target_merge_commit);View on GitHub (pinned to caf1f223d3)
Solutions
- Downcast the error to `ConflictErrorContext` and use its `paths` to tell the user exactly which files conflict
- Have the user resolve those paths in their branches/workspace, then retry the operation
- If operating programmatically, pre-check overlapping path modifications between the parents before attempting the merge
- Do not retry unchanged — content conflicts are deterministic until the underlying edits change
Example fix
// before
let merge_id = but_rebase::merge::octopus(repo, commit, &mut graph)?; // opaque conflict error
// after
let merge_id = match octopus(repo, commit, &mut graph) {
Ok(id) => id,
Err(err) => {
if let Some(ctx) = err.downcast_ref::<ConflictErrorContext>() {
return Err(err.context(format!("conflicted files: {:?}", ctx.paths)));
}
return Err(err);
}
}; Defensive patterns
Strategy: try-catch
Type guard
fn is_conflict_error(err: &anyhow::Error) -> bool {
err.downcast_ref::<ConflictErrorContext>().is_some()
} Try / catch
match octopus(repo, commit, &mut graph) {
Ok(id) => Ok(id),
Err(err) if err.downcast_ref::<ConflictErrorContext>().is_some() => {
let paths: Vec<_> = err
.downcast_ref::<ConflictErrorContext>()
.map(|c| c.paths.clone())
.unwrap_or_default();
Err(err.context(format!("merge conflicts in: {paths:?}"))) // surface to conflict-resolution UI
}
Err(err) => Err(err),
} Prevention
- Always downcast for ConflictErrorContext instead of string-matching the message
- Surface conflicted paths to the user immediately; conflicts need edits, not retries
- Before integrating many branches, check for overlapping modified paths between them
When it happens
Trigger: Rebasing/re-merging a merge commit whose parent trees modify the same lines incompatibly (classic content conflict); merging more than two parents where an intermediate result conflicts with the next tree; changes to the same file on multiple branches being integrated into one workspace merge commit.
Common situations: GitButler integrating multiple virtual branches that touch the same lines; rebasing a merge commit across a base where one side edited a file the other deleted or rewrote; binary files modified in multiple parents.
Related errors
- When using OpenRouter, you must provide a valid API key
- Error ${error.response.status}: ${error.message}
- Invalid message format
- Failed to merge bases while cherry picking commit {target}.
- merge conflict when computing workspace tree
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/0667d9a47d5c7769.
Report an issue: GitHub.