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

  1. Inspect result.Conflicts to see which fields conflict.
  2. Re-run sync with --strategy ours to keep local values, or --strategy theirs to take the peer's values.
  3. Reduce divergence by syncing more frequently so fewer fields conflict.
  4. 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

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


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/a33d9423accd5e0a. Report an issue: GitHub.