gastownhall/beads · error

failed to pull from peer %s: %w

Error message

failed to pull from peer %s: %w

What it means

This error wraps the underlying failure of a DOLT_PULL against a named peer remote. It is thrown by peerPullOutcome only when the pull failed with a real error — not a merge-conflict outcome (those are returned as data via MergeConflictsError or GetConflicts). The %w wrap preserves the root cause (network, auth, remote missing, etc.) for errors.Is/As inspection.

Source

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

// as data for the caller, anything else stays an error. The SQL route rolls
// the conflicted merge back before returning, so its conflicts arrive only via
// MergeConflictsError, captured pre-rollback (bd-578h9.15); the CLI route's
// subprocess writes conflicts to the on-disk working set where GetConflicts
// still sees them.
func (s *DoltStore) peerPullOutcome(ctx context.Context, peer string, pullErr error, conflicts *[]storage.Conflict) error {
	if pullErr == nil {
		return nil
	}
	var mce *versioncontrolops.MergeConflictsError
	if errors.As(pullErr, &mce) {
		*conflicts = mce.Conflicts
		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.

View on GitHub (pinned to 71377f2769)

Solutions

  1. Run `bd dolt pull <peer>` (or dolt fetch) manually against the peer URL to see the raw underlying error
  2. Verify the peer remote exists and its URL is reachable: `dolt remote -v`, then `dolt ls-remote <remote>`
  3. Refresh or re-add stored credentials for the peer so withPeerCredentials supplies valid auth
  4. If the pull rolled back due to divergence, pull with a resolve strategy or fetch+merge explicitly to surface conflicts
  5. Ensure the dolt binary is installed and on PATH when the CLI route is used

Example fix

// before: opaque failure
conflicts, err := store.PullFrom(ctx, "origin")
// after: pre-check remote and surface the wrapped cause
var targetErr *versioncontrolops.MergeConflictsError
if err != nil && !errors.As(err, &targetErr) {
    log.Fatalf("peer pull failed: %v", err) // %w chain shows root cause
}
Defensive patterns

Strategy: try-catch

Validate before calling

remotes, err := store.ListRemotes(ctx)
if err != nil || !containsRemote(remotes, peer) {
    return fmt.Errorf("peer %q not configured; add it first", peer)
}

Type guard

func isConflictOutcome(err error) bool {
    var mce *versioncontrolops.MergeConflictsError
    return errors.As(err, &mce)
} // if false and err != nil, it's a hard pull failure

Try / catch

conflicts, err := store.PullFrom(ctx, peer)
var mce *versioncontrolops.MergeConflictsError
switch {
case err == nil:
    // clean pull
case errors.As(err, &mce):
    // handle conflicts as data
default:
    return fmt.Errorf("peer pull %s unavailable: %w", peer, err)
}

Prevention

When it happens

Trigger: Calling PullFrom(ctx, peer) (or Sync's internal pull path) where DOLT_PULL or the CLI dolt pull exits with an error that is neither a MergeConflictsError nor leaves readable rows in dolt_conflicts — e.g. unknown remote, unreachable URL, bad credentials, non-fast-forward divergence.

Common situations: Peer URL points at a repo that was deleted or renamed; SSH/HTTPS credentials expired or absent; the peer branch diverged so the merge rolls back with no conflict rows (e.g. FK delete-vs-insert divergence noted in the code); dolt CLI binary missing when the CLI route is selected.

Related errors


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