gastownhall/beads · error

failed to commit conflict resolution: %w

Error message

failed to commit conflict resolution: %w

What it means

After auto-resolving conflicts, Sync must commit the resolution with CommitMergeResolution (which includes config changes; plain Commit excludes them per GH#2455). If that commit fails, the merge stays unconcluded, the working set stays dirty, and the next sync would re-wedge. The error wraps the commit cause.

Source

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

			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.
		if err := s.RecomputeBlockedAfterMerge(ctx, beforeCommit); err != nil {
			result.Error = fmt.Errorf("conflicts resolved but is_blocked recompute failed: %w", err)
			return result, result.Error
		}
	}
	result.Merged = true

	// Count pulled commits
	afterCommit, _ := s.GetCurrentCommit(ctx) // Best effort: empty commit hash means diff won't be logged
	if beforeCommit != afterCommit {
		result.PulledCommits = 1 // Simplified - could count actual commits
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped commit error and `dolt status` to see remaining working-set state
  2. Re-run sync: the code path re-enters, re-commits any pending changes (commitBeforePull), and completes the resolution commit
  3. Serialize syncs (single-flight lock) to prevent concurrent writers from dirtying the tree
  4. Check dolt server health, disk space, and connection stability
  5. If wedged, manually commit the resolution (`dolt add -A && dolt commit`) then re-sync

Example fix

// before: parallel agents syncing
// after: serialize
var syncMu sync.Mutex
func safeSync(s *storage.DoltStore, peer string) error {
    syncMu.Lock(); defer syncMu.Unlock()
    _, err := s.Sync(context.Background(), peer, "theirs", nil)
    return err
}
Defensive patterns

Strategy: validation

Validate before calling

// pre-check: writable store, no concurrent sync
if store == nil || isReadOnly() || syncInFlight() {
    return ErrSyncBusy
}

Try / catch

result, err := store.Sync(ctx, peer, strategy, opts)
if err != nil && strings.Contains(err.Error(), "failed to commit conflict resolution") {
    // working set may be wedged; inspect and re-sync
    inspectDoltStatus(); retrySync(strategy)
}

Prevention

When it happens

Trigger: store.Sync with a strategy where all ResolveConflicts calls succeed but CommitMergeResolution(ctx, msg) errors — SQL failure, lock contention, disk full, or a concurrent writer dirtying the working set between resolution and commit.

Common situations: Long conflict-resolution windows during which another process wrote changes; dolt server connection dropped; read-only filesystem; two syncs racing so the second invalidates the first's merge state.

Related errors


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