gastownhall/beads · error

pull succeeded but is_blocked recompute failed: %w

Error message

pull succeeded but is_blocked recompute failed: %w

What it means

The peer pull itself succeeded and produced no conflicts, but the post-merge is_blocked recompute (recomputeBlockedAfterPull) failed. finishPeerPull wraps that recompute error so the caller knows the data was pulled but derived is_blocked fields may be stale. This is a consistency error, not a data-loss error.

Source

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

		return nil
	}
	if c, conflictErr := s.GetConflicts(ctx); conflictErr == nil && len(c) > 0 {
		*conflicts = c
		return nil
	}
	return fmt.Errorf("failed to pull from peer %s: %w", peer, pullErr)
}

// finishPeerPull runs the post-merge is_blocked recompute (bd-6dnrw.3) after a
// successful, conflict-free peer pull and passes the pull result through
// otherwise. Conflicted pulls skip the recompute: the caller resolves the
// conflicts first, and the next sync picks the rows up.
func (s *DoltStore) finishPeerPull(ctx context.Context, conflicts []storage.Conflict, pullErr error, preHead string) ([]storage.Conflict, error) {
	if pullErr != nil || len(conflicts) > 0 || s.readOnly {
		return conflicts, pullErr
	}
	if err := s.recomputeBlockedAfterPull(ctx, preHead); err != nil {
		return conflicts, fmt.Errorf("pull succeeded but is_blocked recompute failed: %w", err)
	}
	return conflicts, nil
}

// Fetch fetches refs from a peer without merging.
// If credentials are stored for this peer, they are used automatically.
// For git-protocol remotes, uses CLI `dolt fetch` to avoid MySQL connection timeouts.
func (s *DoltStore) Fetch(ctx context.Context, peer string) error {
	if useCLI, err := s.prepareCLIRouteForPeerGitProtocol(ctx, peer); err != nil {
		return err
	} else if useCLI {
		return s.withPeerCredentials(ctx, peer, func(creds *remoteCredentials) error {
			return s.doltCLIFetchFromPeer(ctx, peer, creds)
		})
	}
	return s.withPeerCredentials(ctx, peer, func(creds *remoteCredentials) error {
		// Credential CLI routing: route fetch through CLI subprocess.
		if useCLI, err := s.prepareCLIRouteForPeerCredentials(ctx, peer, creds); err != nil {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped cause (%w chain) — it names the failing recompute query/statement
  2. Re-run a sync after the transient DB issue clears; the recompute is idempotent over the merge window
  3. Manually trigger the blocked recompute (or next sync) to refresh is_blocked values
  4. Check dolt server logs for the failing statement around the pull timestamp

Example fix

// before: treating as fatal pull failure
close(stCh)
// after: treat pull as done, schedule recompute retry
conflicts, err := store.PullFrom(ctx, peer)
if err != nil && strings.Contains(err.Error(), "is_blocked recompute failed") {
    log.Warnf("pulled but recompute failed, will retry: %v", err)
}
Defensive patterns

Strategy: retry

Validate before calling

if store == nil || storeIsReadOnly() { skipPullRecomputeExpectations() }

Type guard

func isRecomputeFailure(err error) bool {
    return err != nil && strings.Contains(err.Error(), "is_blocked recompute failed")
}

Try / catch

conflicts, err := store.PullFrom(ctx, peer)
if isRecomputeFailure(err) {
    log.Warn("pull ok, recompute failed; retrying later")
    scheduleRecompute()
    err = nil
}

Prevention

When it happens

Trigger: PullFrom completes a clean pull on a writable store (not readOnly, no conflicts), then recomputeBlockedAfterPull(ctx, preHead) returns an error — typically a SQL failure while recomputing blocked-issue state over the merge diff.

Common situations: Database under load or locked during a long sync; SQL timeout during the recompute; schema drift between replicas affecting the blocked-recompute query; read-only/store contention after automated pulls.

Related errors


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