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 sharedView on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped pullErr for the concrete cause (auth, network, merge)
- Verify remote credentials and connectivity (test the remote URL directly)
- If the remote diverged, reconcile manually (dolt pull/merge, resolve, then retry bd sync)
- 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
- Refresh remote tokens before they expire
- Verify remote URL and network/VPN before long sync operations
- Don't force-push remote history that replicas rely on
- Ensure conflicts from a previous run are cleared so pull can merge
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.