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

  1. Verify the peer name is correct and exists via ListFederationPeersInTx before the lookup
  2. Call AddFederationPeerInTx to register the missing peer if it should exist
  3. 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

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


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