gastownhall/beads · error

conflict resolution failed for %s: %w

Error message

conflict resolution failed for %s: %w

What it means

Sync attempted to auto-resolve each conflict via ResolveConflicts(ctx, c.Field, strategy) and one of those calls failed. The error names the offending field (%s) and wraps the cause. Resolution is per-field, so partial resolution may have occurred for earlier fields.

Source

Thrown at internal/storage/dolt/federation.go:394

	if err != nil {
		result.Error = fmt.Errorf("merge failed: %w", err)
		return result, result.Error
	}

	// Step 4: Handle conflicts if any
	if len(conflicts) > 0 {
		result.Conflicts = conflicts

		if strategy == "" {
			// No strategy specified, leave conflicts for manual resolution
			result.Error = fmt.Errorf("merge conflicts require resolution (use --strategy ours|theirs)")
			return result, result.Error
		}

		// Auto-resolve using strategy
		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

		// Commit the resolution INCLUDING config: the operator chose this
		// strategy, and plain Commit excludes config (GH#2455). A config-only
		// conflict — routine now that kv.memory.* memories sync through config —
		// would otherwise resolve but never commit, leaving the merge
		// unconcluded and re-wedging the next sync.
		if err := s.CommitMergeResolution(ctx, fmt.Sprintf("Resolve conflicts from %s using %s strategy", peer, strategy)); err != nil {
			result.Error = fmt.Errorf("failed to commit conflict resolution: %w", err)
			return result, result.Error
		}

		// bd-578h9.11: the conflicted merge skipped the automatic is_blocked
		// recompute (unresolved rows would have fed it garbage); now that the
		// resolution is committed, cover the whole merge+resolution window.

View on GitHub (pinned to 71377f2769)

Solutions

  1. Note the named field and inspect `dolt conflicts` / result.Conflicts for its state
  2. Re-run the sync with the same strategy — resolution is re-attempted per remaining conflict
  3. Check for concurrent processes touching dolt_conflicts and serialize syncs
  4. Verify the strategy is exactly "ours" or "theirs" and the local schema matches the peer's
  5. If schema drift is the cause, pull the peer's schema changes first (fetch+merge on a clean tree)
Defensive patterns

Strategy: try-catch

Validate before calling

if strategy != "ours" && strategy != "theirs" {
    return fmt.Errorf("strategy must be ours|theirs, got %q", strategy)
}

Try / catch

result, err := store.Sync(ctx, peer, strategy, opts)
if err != nil && strings.Contains(err.Error(), "conflict resolution failed for") {
    field := extractField(err) // parse %s from message or read result.Conflicts
    log.Printf("resolution failed on field %s; retrying sync", field)
    retrySync(strategy)
}

Prevention

When it happens

Trigger: store.Sync(ctx, peer, "ours"|"theirs", opts) where a conflicted merge exists and ResolveConflicts errors on some field — e.g. the field no longer exists, SQL failure writing the resolution, or strategy value invalid at the storage layer.

Common situations: Schema drift between replicas so a conflicted field is unknown locally; DB lock/timeout mid-resolution; strategy string corrupted before reaching the store; conflict rows already cleaned up by a concurrent process.

Related errors


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