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

  1. Check the wrapped scan error for which column/type mismatched
  2. Run migrations so the stored schema matches this binary's expectations
  3. Inspect the offending row and fix unexpected NULLs or data types
  4. 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

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


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