gastownhall/beads · error
scan federation peer: %w
Error message
scan federation peer: %w
What it means
A row from the federation_peers listing could not be scanned into FederationPeerRow; the error is wrapped as "scan federation peer". This almost always means the database schema doesn't match the code's expectations (column type or count mismatch) or a row holds a value incompatible with the destination field (e.g. NULL where not allowed).
Source
Thrown at internal/storage/issueops/federation.go:114
SELECT name, remote_url, username, password_encrypted, sovereignty, last_sync, created_at, updated_at
FROM federation_peers ORDER BY name
`)
if err != nil {
return nil, fmt.Errorf("list federation peers: %w", err)
}
defer rows.Close()
var peers []*FederationPeerRow
for rows.Next() {
var row FederationPeerRow
var lastSync sql.NullTime
var username sql.NullString
if err := rows.Scan(
&row.Peer.Name, &row.Peer.RemoteURL, &username, &row.EncryptedPwd,
&row.Peer.Sovereignty, &lastSync, &row.Peer.CreatedAt, &row.Peer.UpdatedAt,
); err != nil {
return nil, fmt.Errorf("scan federation peer: %w", err)
}
if username.Valid {
row.Peer.Username = username.String
}
if lastSync.Valid {
row.Peer.LastSync = &lastSync.Time
}
peers = append(peers, &row)
}
return peers, rows.Err()
}
// RemoveFederationPeerInTx deletes a federation peer by name.
func RemoveFederationPeerInTx(ctx context.Context, tx *sql.Tx, name string) error {
_, err := tx.ExecContext(ctx, "DELETE FROM federation_peers WHERE name = ?", name)
if err != nil {View on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped scan error for which column/type mismatched
- Run migrations so the stored schema matches this binary's expectations
- Inspect the offending row and fix unexpected NULLs or data types
- Rebuild the peer record: RemoveFederationPeerInTx then AddFederationPeerInTx if a row is corrupt
Example fix
// before
// schema has old column: password (plaintext) but code scans password_encrypted
peers, err := issueops.ListFederationPeersInTx(ctx, tx) // scan mismatch
// after
if err := migrate(ctx, db); err != nil { // renames password -> password_encrypted
return err
}
peers, err := issueops.ListFederationPeersInTx(ctx, tx) Defensive patterns
Strategy: try-catch
Validate before calling
// verify expected columns before scanning rows, err := tx.QueryContext(ctx, `SELECT name, remote_url, username, password_encrypted, sovereignty, last_sync, created_at, updated_at FROM federation_peers LIMIT 1`)
Try / catch
peers, err := issueops.ListFederationPeersInTx(ctx, tx)
if err != nil {
if strings.Contains(err.Error(), "Scan") || strings.Contains(err.Error(), "converting") {
return fmt.Errorf("schema drift detected; run migrations: %w", err)
}
return err
} Prevention
- Run migrations on every deploy so schema and binary match
- Avoid manual schema edits; treat migration files as the only schema source
- Null-check rows inserted outside the library before listing
When it happens
Trigger: rows.Scan fails for a peer row: column count/type mismatch after a schema change, or an unexpected NULL/data type in name, remote_url, username, password_encrypted, sovereignty, last_sync, created_at, or updated_at.
Common situations: Upgrading/downgrading beads so the code and stored schema diverge; manual schema edits; corrupted or hand-inserted rows with unexpected NULLs.
Related errors
- failed to recompute is_blocked: %w
- get issue: %w
- failed to begin transaction: %w
- failed to commit is_blocked repairs: %w
- failed to query orphaned dependencies: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/510598bb94210d4c.
Report an issue: GitHub.