gastownhall/beads · error
failed to fetch from peer %s: %w
Error message
failed to fetch from peer %s: %w
What it means
Wraps a failure of `CALL DOLT_FETCH(?)` (or the CLI fetch path) when fetching refs from a peer remote without merging. Fetch is the first step of every sync, so this error usually blocks synchronization entirely. The wrapped cause distinguishes network failures, unknown remotes, and auth problems.
Source
Thrown at internal/storage/dolt/federation.go:190
// For git-protocol remotes, uses CLI `dolt fetch` to avoid MySQL connection timeouts.
func (s *DoltStore) Fetch(ctx context.Context, peer string) error {
if useCLI, err := s.prepareCLIRouteForPeerGitProtocol(ctx, peer); err != nil {
return err
} else if useCLI {
return s.withPeerCredentials(ctx, peer, func(creds *remoteCredentials) error {
return s.doltCLIFetchFromPeer(ctx, peer, creds)
})
}
return s.withPeerCredentials(ctx, peer, func(creds *remoteCredentials) error {
// Credential CLI routing: route fetch through CLI subprocess.
if useCLI, err := s.prepareCLIRouteForPeerCredentials(ctx, peer, creds); err != nil {
return err
} else if useCLI {
return s.doltCLIFetchFromPeer(ctx, peer, creds)
}
return withEnvCredentials(creds, func() error {
if err := s.execWithLongTimeout(ctx, "CALL DOLT_FETCH(?)", peer); err != nil {
return fmt.Errorf("failed to fetch from peer %s: %w", peer, err)
}
return nil
})
})
}
// ListRemotes returns configured remote names and URLs.
func (s *DoltStore) ListRemotes(ctx context.Context) ([]storage.RemoteInfo, error) {
return versioncontrolops.ListRemotes(ctx, s.db)
}
// hasPersistedCLIRemote reports whether a Dolt remote is persisted on disk in
// .dolt/repo_state.json — in the database CLI directory (CLIDir) or the dolt
// server root (Path, per GH#2118). A freshly (auto-)started sql-server can
// report an empty dolt_remotes table at store open even though remotes are
// persisted on disk. The #4259 remote-migrate gate therefore consults this
// directly so a cold-start open cannot miss the remote and migrate the shared
// database in place.View on GitHub (pinned to 71377f2769)
Solutions
- Check network reachability of the peer URL (`dolt ls-remote <remote>` or curl the URL)
- Verify the remote name/URL with `dolt remote -v` and re-add if renamed: `dolt remote set-url <name> <url>`
- Refresh stored credentials for the peer; confirm env credentials via withEnvCredentials (GITHUB_TOKEN etc.)
- If the SQL route times out, force the CLI route (the code prefers it for git-protocol remotes) or increase the long-timeout window
- Retry after transient outage — fetch is safe to re-run
Example fix
// before
if err := store.Fetch(ctx, "origin"); err != nil { panic(err) }
// after: retry transient fetch failures
err := store.Fetch(ctx, "origin")
for i := 0; err != nil && i < 3; i++ {
time.Sleep(time.Duration(1<<i) * time.Second)
err = store.Fetch(ctx, "origin")
} Defensive patterns
Strategy: retry
Validate before calling
remotes, _ := store.ListRemotes(ctx)
if !remoteExists(remotes, peer) { return fmt.Errorf("unknown peer %q", peer) }
// plus network probe:
if err := probeRemoteURL(peerURL); err != nil { return err } Type guard
func isFetchable(peer string) bool { return peer != "" && remoteConfigured(peer) } Try / catch
if err := store.Fetch(ctx, peer); err != nil {
if isTransient(err) { // network/timeout
return retryWithBackoff(3, func() error { return store.Fetch(ctx, peer) })
}
return fmt.Errorf("fetch %s: %w", peer, err)
} Prevention
- Probe peer reachability before scheduled syncs
- Rotate credentials before expiry; store them via the library's credential path
- Prefer the CLI fetch route for git-protocol remotes to avoid MySQL timeouts
- Alert on repeated fetch failures at the same peer URL
When it happens
Trigger: Calling Fetch(ctx, peer) or Sync's Step 1 where DOLT_FETCH against the peer's remote errors — unreachable URL, TLS failure, unknown remote name, invalid credentials, or CLI fetch subprocess failure.
Common situations: Offline or firewalled environment; peer git server (GitHub/Forgejo) down or rate-limiting; remote name changed after re-cloning; expired token in stored credentials; MySQL connection timeout on the SQL route (why the CLI route exists).
Related errors
- fetch failed: %w
- failed to push to peer %s: %w
- failed to pull from peer %s: %w
- fetch from %s/%s: %w
- fetch failed: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/017f16b97c0a2081.
Report an issue: GitHub.