gastownhall/beads · info · storage.ErrNotFound
%w: federation peer %s
Error message
%w: federation peer %s
What it means
GetFederationPeerInTx scanned no row for the requested peer name, so the function returns storage.ErrNotFound wrapped with the peer name. This is the library's canonical not-found signal for federation peers — callers should detect it with errors.Is. It means the name simply does not exist in federation_peers, not a database failure.
Source
Thrown at internal/storage/issueops/federation.go:77
}
// GetFederationPeerInTx retrieves a federation peer by name.
// Returns storage.ErrNotFound (wrapped) if the peer does not exist.
func GetFederationPeerInTx(ctx context.Context, tx *sql.Tx, name string) (*FederationPeerRow, error) {
var row FederationPeerRow
var lastSync sql.NullTime
var username sql.NullString
err := tx.QueryRowContext(ctx, `
SELECT name, remote_url, username, password_encrypted, sovereignty, last_sync, created_at, updated_at
FROM federation_peers WHERE name = ?
`, name).Scan(
&row.Peer.Name, &row.Peer.RemoteURL, &username, &row.EncryptedPwd,
&row.Peer.Sovereignty, &lastSync, &row.Peer.CreatedAt, &row.Peer.UpdatedAt,
)
if err == sql.ErrNoRows {
return nil, fmt.Errorf("%w: federation peer %s", storage.ErrNotFound, name)
}
if err != nil {
return nil, fmt.Errorf("get federation peer: %w", err)
}
if username.Valid {
row.Peer.Username = username.String
}
if lastSync.Valid {
row.Peer.LastSync = &lastSync.Time
}
return &row, nil
}
// ListFederationPeersInTx returns all configured federation peer rows.
func ListFederationPeersInTx(ctx context.Context, tx *sql.Tx) ([]*FederationPeerRow, error) {
rows, err := tx.QueryContext(ctx, `View on GitHub (pinned to 71377f2769)
Solutions
- Verify the peer name is correct and exists via ListFederationPeersInTx before the lookup
- Call AddFederationPeerInTx to register the missing peer if it should exist
- Branch on errors.Is(err, storage.ErrNotFound) to treat absence as a normal case rather than a hard failure
Example fix
// before
peer, err := issueops.GetFederationPeerInTx(ctx, tx, name) // hard-fails on missing peer
// after
peer, err := issueops.GetFederationPeerInTx(ctx, tx, name)
if errors.Is(err, storage.ErrNotFound) {
return createOrPromptForPeer(name) // expected case
}
if err != nil {
return err
} Defensive patterns
Strategy: type-guard
Validate before calling
peers, err := issueops.ListFederationPeersInTx(ctx, tx)
if err == nil {
for _, p := range peers {
if p.Peer.Name == name {
return nil // peer exists
}
}
} Type guard
func isPeerNotFound(err error) bool {
return errors.Is(err, storage.ErrNotFound)
} Try / catch
peer, err := issueops.GetFederationPeerInTx(ctx, tx, name)
if isPeerNotFound(err) {
return handleMissingPeer(name) // normal branch, not a failure
}
if err != nil {
return err
} Prevention
- Treat storage.ErrNotFound (via errors.Is) as a normal control-flow signal
- Cross-check peer names with ListFederationPeersInTx in admin tooling
- Standardize peer-name input through one validated config source
When it happens
Trigger: Calling GetFederationPeerInTx with a peer name that was never added, or that was deleted via RemoveFederationPeerInTx; querying on a different database than the one where the peer was registered.
Common situations: Typo'd peer name in CLI/config; syncing against a fresh clone whose federation_peers table is empty; peer removed by another machine before this lookup.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- %w: federation peer %s
- no store is open for this workspace
- no store available
- molecule %s not found: %w
- issue %s not found
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/a57202faca736c47.
Report an issue: GitHub.