gastownhall/beads · error

conflicts resolved but is_blocked recompute failed: %w

Error message

conflicts resolved but is_blocked recompute failed: %w

What it means

A conflicted merge skips the automatic is_blocked recompute (unresolved rows would feed it garbage), so once the resolution is committed, Sync calls RecomputeBlockedAfterMerge(ctx, beforeCommit) to cover the whole merge+resolution window (bd-578h9.11). Failure here is wrapped as this error: conflicts are resolved and committed, but the blocked-status derived data may be stale.

Source

Thrown at internal/storage/embeddeddolt/federation.go:359

		}
		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 {
			result.Error = fmt.Errorf("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

	afterCommit, _ := s.GetCurrentCommit(ctx)
	if beforeCommit != afterCommit {
		result.PulledCommits = 1
	}

	// Step 5: Push
	if err := s.PushTo(ctx, peer); err != nil {
		result.PushError = err
	} else {
		result.Pushed = true
	}

	// Record last sync time in metadata.

View on GitHub (pinned to 71377f2769)

Solutions

  1. Manually trigger the blocked recompute (e.g. via the admin/doctor path) to refresh is_blocked.
  2. Re-run sync — the recompute is idempotent once the resolution is committed.
  3. Inspect dependency rows introduced by the merge for cycles or dangling references.
  4. Verify is_blocked values manually if the recompute keeps failing and file an issue.

Example fix

// before: stale is_blocked after a failed recompute
result, err := store.Sync(ctx, peer, "ours")
// after: force a recompute if sync reports the failure
result, err := store.Sync(ctx, peer, "ours")
if err != nil && strings.Contains(err.Error(), "is_blocked recompute failed") {
    if rerr := store.RecomputeBlocked(ctx); rerr == nil {
        err = nil // derived data repaired; merge itself succeeded
    }
}
Defensive patterns

Strategy: fallback

Validate before calling

// sanity-check dependency rows the merge will introduce
for _, dep := range incomingDeps {
    if dangling(dep) || cycleWouldForm(dep) {
        return fmt.Errorf("fix dependency %s before sync", dep)
    }
}

Try / catch

result, err := store.Sync(ctx, peer, "ours")
if err != nil && strings.Contains(err.Error(), "is_blocked recompute failed") {
    // merge itself succeeded; repair derived data out-of-band
    if rerr := store.RecomputeBlocked(ctx); rerr == nil {
        err = nil
    }
}

Prevention

When it happens

Trigger: store.Sync(ctx, peer, strategy) completing conflict resolution successfully, then RecomputeBlockedAfterMerge errors — SQL failure during the recompute, a dependency-cycle detection error, or the transaction touching rows mutated concurrently.

Common situations: Large merge with many dependency edges making the recompute slow or resource-constrained; concurrent writer modifying dependencies mid-recompute; pre-existing bad dependency rows that trip cycle validation.

Related errors


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