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
- Use errors.Is(err, storage.ErrNotFound) to branch on this condition rather than string matching.
- List configured peers (ListFederationPeers) to see the exact registered names.
- Add the missing peer with AddFederationPeer if it should exist.
- 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
- Use errors.Is(err, storage.ErrNotFound), never string matching
- Confirm exact name spelling/case via ListFederationPeers
- Re-add peers on fresh clones — credentials table is workspace-local
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.