gastownhall/beads · warning · storage.ErrNotFound

%w: federation peer %s

Error message

%w: federation peer %s

What it means

GetFederationPeer wraps storage.ErrNotFound when the query for the named peer returns sql.ErrNoRows. This is the library's sentinel for 'peer not registered' — callers should use errors.Is to detect it. It is an expected condition, not corruption: the peer name simply has no row in federation_peers.

Source

Thrown at internal/storage/dolt/credentials.go:336

	return nil
}

// GetFederationPeer retrieves a federation peer by name.
// Returns storage.ErrNotFound (wrapped) if the peer does not exist.
func (s *DoltStore) GetFederationPeer(ctx context.Context, name string) (*storage.FederationPeer, error) {
	var peer storage.FederationPeer
	var encryptedPwd []byte
	var lastSync sql.NullTime
	var username sql.NullString

	err := s.db.QueryRowContext(ctx, `
		SELECT name, remote_url, username, password_encrypted, sovereignty, last_sync, created_at, updated_at
		FROM federation_peers WHERE name = ?
	`, name).Scan(&peer.Name, &peer.RemoteURL, &username, &encryptedPwd, &peer.Sovereignty, &lastSync, &peer.CreatedAt, &peer.UpdatedAt)

	if err == sql.ErrNoRows {
		return nil, fmt.Errorf("%w: federation peer %s", storage.ErrNotFound, name)
	}
	if err != nil {
		return nil, fmt.Errorf("failed to get federation peer: %w", err)
	}

	if username.Valid {
		peer.Username = username.String
	}
	if lastSync.Valid {
		peer.LastSync = &lastSync.Time
	}

	// Decrypt password
	if len(encryptedPwd) > 0 {
		if err := s.ensureCredentialKey(ctx); err != nil {
			return nil, fmt.Errorf("failed to initialize credential key: %w", err)
		}
		peer.Password, err = s.decryptPassword(encryptedPwd)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Use errors.Is(err, storage.ErrNotFound) to branch on this condition rather than string matching.
  2. List configured peers (ListFederationPeers) to see the exact registered names.
  3. Add the missing peer with AddFederationPeer if it should exist.
  4. Check name spelling and case — lookup is exact-match SQL.

Example fix

// before
peer, err := store.GetFederationPeer(ctx, name)
if err != nil { return err }
// after
peer, err := store.GetFederationPeer(ctx, name)
if errors.Is(err, storage.ErrNotFound) {
    return fmt.Errorf("peer %q not configured; run: bd federation add-peer %s <url>", name, name)
}
if err != nil { return err }
Defensive patterns

Strategy: try-catch

Validate before calling

peers, _ := store.ListFederationPeers(ctx)
for _, p := range peers { if p.Name == name { /* exists */ } }

Type guard

func isPeerNotFound(err error) bool { return errors.Is(err, storage.ErrNotFound) }

Try / catch

peer, err := store.GetFederationPeer(ctx, name)
if isPeerNotFound(err) {
    return fmt.Errorf("peer %q not configured; add it with AddFederationPeer", name)
}
if err != nil { return err }

Prevention

When it happens

Trigger: Calling GetFederationPeer (directly or via withPeerCredentials during push/pull) with a name that was never added via AddFederationPeer, was removed via RemoveFederationPeer, or is misspelled/differently-cased.

Common situations: Typos in `bd federation sync <peer>` style invocations; peers removed on another machine (credentials are local, not synced via refs/dolt/data for this table's absence in that flow); case-sensitive name mismatch ('Team-A' vs 'team-a').

Related errors


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