gastownhall/beads · error

pull from %s: %w

Error message

pull from %s: %w

What it means

Returned when the actual Dolt pull/merge operation against a peer fails with an error that is NOT a MergeConflictsError. Conflicts are captured and returned as data; anything else (network failure to the peer remote, auth failure, missing remote, merge implementation error) is wrapped here with the peer name for context.

Source

Thrown at internal/storage/embeddeddolt/version_control.go:653

	if _, err := s.CommitPending(ctx, "beads"); err != nil {
		return nil, fmt.Errorf("commit pending before pull: %w", err)
	}

	preHead := s.preMergeHead(ctx)
	var conflicts []storage.Conflict
	err := s.withPeerAuth(ctx, peer, func(user string) error {
		return s.withMutatingPinnedDBConn(ctx, func(db versioncontrolops.DBConn) error {
			if pullErr := versioncontrolops.Pull(ctx, db, peer, s.branch, user); pullErr != nil {
				// bd-578h9.15: the settle machinery aborts a merge it cannot
				// auto-resolve before returning, so dolt_conflicts is already
				// empty here; the conflicts arrive captured pre-abort inside
				// MergeConflictsError instead.
				var mce *versioncontrolops.MergeConflictsError
				if errors.As(pullErr, &mce) {
					conflicts = mce.Conflicts
					return nil
				}
				return fmt.Errorf("pull from %s: %w", peer, pullErr)
			}
			return nil
		})
	})
	if err != nil || len(conflicts) > 0 {
		// Conflicted pulls skip the recompute: the operator resolves first,
		// and the next sync picks the rows up.
		return conflicts, err
	}
	if err := s.recomputeBlockedAfterPull(ctx, preHead); err != nil {
		return conflicts, fmt.Errorf("pull succeeded but is_blocked recompute failed: %w", err)
	}
	return conflicts, nil
}

// preMergeHead reads the pre-pull HEAD for the post-merge is_blocked
// recompute (bd-6dnrw.3). Empty on failure, which degrades the recompute to a
// full pass instead of skipping the hook.

View on GitHub (pinned to 71377f2769)

Solutions

  1. Verify the peer remote exists and its URL is correct before pulling
  2. Check network connectivity and credentials for the peer
  3. Run `bd doctor` / inspect the underlying wrapped error (%%w chain) for the root cause
  4. Retry after the peer is back online; investigate if the wrapped error persists
Defensive patterns

Strategy: retry

Validate before calling

// verify peer connectivity/remote before pulling
if _, err := os.Stat(peerPath); err != nil {
    return fmt.Errorf("peer unreachable before pull: %%w", err)
}

Try / catch

if err := store.PullFrom(ctx, peer); err != nil {
    var mce *versioncontrolops.MergeConflictsError
    if errors.As(err, &mce) { return handleConflicts(mce.Conflicts) }
    return fmt.Errorf("transient pull failure, retry later: %%w", err)
}

Prevention

When it happens

Trigger: PullFrom(ctx, peer) and the inner withPeerAuth/withMutatingPinnedDBConn pull returns a non-conflict error: peer remote unreachable, invalid credentials, unknown remote name, or an unexpected dolt merge failure.

Common situations: Peer URL misconfigured or peer offline during sync; revoked or expired peer credentials; database not registered as a remote; Dolt version mismatch producing merge engine errors.

Related errors


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