gastownhall/beads · error

fetch failed: %w

Error message

fetch failed: %w

What it means

Sync fetches the peer remote before merging. Any failure from store.Fetch(ctx, peer) — network unreachable, unknown remote, bad refspec, or Dolt fetch internals — is wrapped as 'fetch failed'. result.Fetched stays false, so the sync aborted before any merge work.

Source

Thrown at internal/storage/embeddeddolt/federation.go:311

	result := &storage.SyncResult{
		Peer:      peer,
		StartTime: time.Now(),
	}

	// GH#2474 / bd-578h9.2: commit pending changes before the merge, matching
	// embedded Pull/PullRemote/PullFrom and server-mode Sync. Embedded Commit is
	// DOLT_COMMIT('-Am'), so it stages config — where kv.memory.* memories live —
	// and a leftover dirty working set (e.g. a `bd remember` write) would
	// otherwise make DOLT_MERGE refuse to start ("cannot merge with uncommitted
	// changes"). CommitPending is a no-op when the working set is already clean.
	if _, err := s.CommitPending(ctx, "beads"); err != nil {
		result.Error = fmt.Errorf("commit pending before sync: %w", err)
		return result, result.Error
	}

	// Step 1: Fetch
	if err := s.Fetch(ctx, peer); err != nil {
		result.Error = fmt.Errorf("fetch failed: %w", err)
		return result, result.Error
	}
	result.Fetched = true

	// Step 2: Get commit before merge for change detection
	beforeCommit, _ := s.GetCurrentCommit(ctx)

	// Step 3: Merge peer's branch
	remoteBranch := fmt.Sprintf("%s/%s", peer, s.branch)
	conflicts, err := s.Merge(ctx, remoteBranch)
	if err != nil {
		result.Error = fmt.Errorf("merge failed: %w", err)
		return result, result.Error
	}

	// Step 4: Handle conflicts
	if len(conflicts) > 0 {
		result.Conflicts = conflicts

View on GitHub (pinned to 71377f2769)

Solutions

  1. Verify the peer remote is registered and reachable (`dolt remote -v`, ping the host).
  2. Check/refresh credentials for the remote transport (SSH keys, tokens).
  3. Retry sync once connectivity is restored — fetch is safe to retry.
  4. Correct the peer name/URL configuration and re-run sync.

Example fix

// before: peer offline -> fetch failed
result, err := store.Sync(ctx, "laptop")
// after: probe reachability first
if err := checkPeerReachable("laptop"); err != nil {
    return fmt.Errorf("defer sync: peer unreachable: %w", err)
}
result, err = store.Sync(ctx, "laptop")
Defensive patterns

Strategy: retry

Validate before calling

// probe the peer before syncing
func peerReachable(peer string) error {
    conn, err := net.DialTimeout("tcp", peerHost(peer), 5*time.Second)
    if err != nil { return err }
    return conn.Close()
}

Try / catch

result, err := store.Sync(ctx, peer)
if err != nil && strings.Contains(err.Error(), "fetch failed") {
    // fetch is idempotent: back off and retry
    time.Sleep(backoff)
    result, err = store.Sync(ctx, peer)
}

Prevention

When it happens

Trigger: store.Sync(ctx, peer) where s.Fetch(ctx, peer) errors: the peer remote is not configured, the peer is offline/unreachable, SSH/HTTPS credentials are missing or expired, or the peer repo path is wrong.

Common situations: Peer machine is down during federation sync; remote URL typo in peer configuration; expired credentials/token; firewall blocking the transport; peer branch was force-removed.

Related errors


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