gastownhall/beads · error

pull: %w

Error message

pull: %w

What it means

The pull step of the sync loop failed with an error that was not a fast-forwardable 'behind remote' race (those are retried as transients). Any other pull error aborts the sync loop immediately.

Source

Thrown at cmd/bd/sync.go:318

		}
		if pullErr != nil {
			// A pull that merged nothing only because the branch is a strict
			// ancestor of a remote tip that moved after our fetch is the pull-side
			// twin of a push race: a peer advanced the remote, and the next pull
			// fast-forwards it. Re-enter the loop instead of hard-failing the tick;
			// if the budget runs out, the retries-exhausted exit tells the timer to
			// try again next tick (same as a push race or a dirty working set). It
			// is classified from the typed sentinel, never the message, so a
			// genuine divergence — which verifyPullLanded leaves unwrapped — falls
			// through to the hard error below.
			if isPullBehindFastForwardableErr(pullErr) {
				out.Transients = append(out.Transients, syncTransient{
					Attempt: attempt, Kind: syncTransientPullBehind, Error: pullErr.Error(),
				})
				ops.report("pull behind a fast-forwardable remote tip (peer pushed after our fetch) — re-pulling and retrying")
				continue
			}
			return out, fmt.Errorf("pull: %w", pullErr)
		}
		if conflictErr != nil {
			return out, fmt.Errorf("conflict check: %w", conflictErr)
		}

		ops.report("recompute-blocked")
		corrected, err := ops.recompute(ctx)
		if err != nil {
			if !isRecomputeDirtyGraphErr(err) {
				return out, fmt.Errorf("recompute-blocked: %w", err)
			}
			// Not our failure and not a durable one: someone else's
			// uncommitted edit to issues/dependencies landed between our pull
			// and our repair. Treat it exactly like a push race — re-enter the
			// attempt loop, and if the budget runs out report the transient
			// exit so the next tick tries again. Classifying it as a hard
			// error instead left local commits unpublished until a tick
			// happened to catch a clean working set, which on a shared

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped pullErr for the concrete cause (auth, network, merge)
  2. Verify remote credentials and connectivity (test the remote URL directly)
  3. If the remote diverged, reconcile manually (dolt pull/merge, resolve, then retry bd sync)
  4. Check for pre-existing live conflicts that block the merge
Defensive patterns

Strategy: retry

Validate before calling

// verify remote reachability and credentials before syncing
if err := checkRemoteAuth(remoteURL); err != nil {
    return fmt.Errorf("remote auth invalid; refresh credentials before bd sync")
}

Try / catch

out, err := runSyncCommand(ctx, opts)
if err != nil {
    var pullErr *PullError
    if errors.As(err, &pullErr) && isNetworkErr(pullErr.Unwrap()) {
        // check network/VPN, then retry with backoff
    }
    return err
}

Prevention

When it happens

Trigger: ops.pull returns a non-race error: remote unreachable/auth failure, Dolt merge refused for reasons other than fast-forward, corrupted refs, or network timeout during fetch.

Common situations: Bad or expired git/Dolt remote credentials; remote URL misconfigured; repository history diverged non-fast-forwardably; network outage or VPN down while syncing with teammates.

Related errors


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