gastownhall/beads · error
merge conflicts require resolution (use --strategy ours|thei
Error message
merge conflicts require resolution (use --strategy ours|theirs)
What it means
When merging a peer branch produces conflicts and no resolution strategy was supplied, Sync stops and returns this error with result.Conflicts populated. The merge is left in a merging state; the caller must re-run sync with an explicit strategy (ours or theirs) to resolve.
Source
Thrown at internal/storage/embeddeddolt/federation.go:332
result.Fetched = true
// Step 2: Get commit before merge for change detection
beforeCommit, _ := s.GetCurrentCommit(ctx)
// Step 3: Merge peer's branch
remoteBranch := fmt.Sprintf("%s/%s", peer, s.branch)
conflicts, err := s.Merge(ctx, remoteBranch)
if err != nil {
result.Error = fmt.Errorf("merge failed: %w", err)
return result, result.Error
}
// Step 4: Handle conflicts
if len(conflicts) > 0 {
result.Conflicts = conflicts
if strategy == "" {
result.Error = fmt.Errorf("merge conflicts require resolution (use --strategy ours|theirs)")
return result, result.Error
}
for _, c := range conflicts {
if err := s.ResolveConflicts(ctx, c.Field, strategy); err != nil {
result.Error = fmt.Errorf("conflict resolution failed for %s: %w", c.Field, err)
return result, result.Error
}
}
result.ConflictsResolved = true
// CommitMergeResolution, not Commit: Commit's GH#3886 nothing-to-commit
// tolerance would swallow the --ours case (resolution dirties nothing)
// as a silent no-op here, leaving dolt_merge_status.is_merging true while
// this function reports result.Merged = true and pushes — the exact
// re-wedge CommitMergeResolution's doc comment describes. See the
// server-mode twin, dolt/federation.go's Sync.
if err := s.CommitMergeResolution(ctx, fmt.Sprintf("Resolve conflicts from %s using %s strategy", peer, strategy)); err != nil {View on GitHub (pinned to 71377f2769)
Solutions
- Inspect result.Conflicts to see which fields conflict.
- Re-run sync with --strategy ours to keep local values, or --strategy theirs to take the peer's values.
- Reduce divergence by syncing more frequently so fewer fields conflict.
- If the merge is left in-progress, resolve or abort it before other operations.
Example fix
// before: conflicts, no strategy result, err := store.Sync(ctx, peer) // after: supply a strategy result, err := store.SyncWithStrategy(ctx, peer, "ours") // or "theirs"
Defensive patterns
Strategy: validation
Validate before calling
// only auto-sync when either no conflicts are expected or a strategy is supplied
func canSyncAutomatically(strategy string) bool {
return strategy == "ours" || strategy == "theirs"
}
if !canSyncAutomatically(strategy) {
return fmt.Errorf("refusing unattended sync without --strategy ours|theirs")
} Try / catch
result, err := store.Sync(ctx, peer)
if err != nil && strings.Contains(err.Error(), "merge conflicts require resolution") {
// conflicts are in result.Conflicts; re-run with an explicit strategy
for _, c := range result.Conflicts {
log.Printf("conflict on field %q", c.Field)
}
result, err = store.SyncWithStrategy(ctx, peer, chosenStrategy)
} Prevention
- Always pass --strategy ours|theirs for unattended sync jobs.
- Sync frequently so fewer fields diverge.
- Review result.Conflicts after every conflicted sync.
- Partition ownership of beads across peers to avoid same-field edits.
When it happens
Trigger: store.Sync(ctx, peer) called with strategy == "" while s.Merge returned non-empty conflicts — both peers edited the same issue field (e.g. title/status) since their last common commit.
Common situations: Two agents/machines updated the same bead offline, then both sync; automated sync jobs omitting the --strategy flag; first sync after long divergence between peers.
Related errors
- commit conflict resolution: %w
- database not available: %w
- no active beads workspace
- failed to persist sync.remote to config.yaml: %w
- failed to set sync remote: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/a33d9423accd5e0a.
Report an issue: GitHub.