{"record":{"id":"a01c8b4b6d6a763f","repo":"gastownhall/beads","slug":"failed-to-pull-from-peer-s-w","errorCode":null,"errorMessage":"failed to pull from peer %s: %w","messagePattern":"failed to pull from peer (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dolt/federation.go","lineNumber":153,"sourceCode":"// as data for the caller, anything else stays an error. The SQL route rolls\n// the conflicted merge back before returning, so its conflicts arrive only via\n// MergeConflictsError, captured pre-rollback (bd-578h9.15); the CLI route's\n// subprocess writes conflicts to the on-disk working set where GetConflicts\n// still sees them.\nfunc (s *DoltStore) peerPullOutcome(ctx context.Context, peer string, pullErr error, conflicts *[]storage.Conflict) error {\n\tif pullErr == nil {\n\t\treturn nil\n\t}\n\tvar mce *versioncontrolops.MergeConflictsError\n\tif errors.As(pullErr, &mce) {\n\t\t*conflicts = mce.Conflicts\n\t\treturn nil\n\t}\n\tif c, conflictErr := s.GetConflicts(ctx); conflictErr == nil && len(c) > 0 {\n\t\t*conflicts = c\n\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"failed to pull from peer %s: %w\", peer, pullErr)\n}\n\n// finishPeerPull runs the post-merge is_blocked recompute (bd-6dnrw.3) after a\n// successful, conflict-free peer pull and passes the pull result through\n// otherwise. Conflicted pulls skip the recompute: the caller resolves the\n// conflicts first, and the next sync picks the rows up.\nfunc (s *DoltStore) finishPeerPull(ctx context.Context, conflicts []storage.Conflict, pullErr error, preHead string) ([]storage.Conflict, error) {\n\tif pullErr != nil || len(conflicts) > 0 || s.readOnly {\n\t\treturn conflicts, pullErr\n\t}\n\tif err := s.recomputeBlockedAfterPull(ctx, preHead); err != nil {\n\t\treturn conflicts, fmt.Errorf(\"pull succeeded but is_blocked recompute failed: %w\", err)\n\t}\n\treturn conflicts, nil\n}\n\n// Fetch fetches refs from a peer without merging.\n// If credentials are stored for this peer, they are used automatically.","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/federation.go#L135-L171","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Run `bd dolt pull <peer>` (or dolt fetch) manually against the peer URL to see the raw underlying error","Verify the peer remote exists and its URL is reachable: `dolt remote -v`, then `dolt ls-remote <remote>`","Refresh or re-add stored credentials for the peer so withPeerCredentials supplies valid auth","If the pull rolled back due to divergence, pull with a resolve strategy or fetch+merge explicitly to surface conflicts","Ensure the dolt binary is installed and on PATH when the CLI route is used"],"exampleFix":"// before: opaque failure\nconflicts, err := store.PullFrom(ctx, \"origin\")\n// after: pre-check remote and surface the wrapped cause\nvar targetErr *versioncontrolops.MergeConflictsError\nif err != nil && !errors.As(err, &targetErr) {\n    log.Fatalf(\"peer pull failed: %v\", err) // %w chain shows root cause\n}","handlingStrategy":"try-catch","validationCode":"remotes, err := store.ListRemotes(ctx)\nif err != nil || !containsRemote(remotes, peer) {\n    return fmt.Errorf(\"peer %q not configured; add it first\", peer)\n}","typeGuard":"func isConflictOutcome(err error) bool {\n    var mce *versioncontrolops.MergeConflictsError\n    return errors.As(err, &mce)\n} // if false and err != nil, it's a hard pull failure","tryCatchPattern":"conflicts, err := store.PullFrom(ctx, peer)\nvar mce *versioncontrolops.MergeConflictsError\nswitch {\ncase err == nil:\n    // clean pull\ncase errors.As(err, &mce):\n    // handle conflicts as data\ndefault:\n    return fmt.Errorf(\"peer pull %s unavailable: %w\", peer, err)\n}","preventionTips":["Verify the peer remote exists and is reachable before pulling (ListRemotes + ls-remote)","Keep peer credentials fresh and test them with a cheap fetch","Sync frequently to avoid non-fast-forward divergence that rolls merges back","Ensure the dolt CLI is installed when relying on the CLI route"],"tags":["dolt","federation","pull","network"],"backgroundTag":"peer-pull-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}