gastownhall/beads · error

add federation peer: %w

Error message

add federation peer: %w

What it means

The INSERT ... ON DUPLICATE KEY UPDATE for the federation_peers row failed at the SQL level; the driver error is wrapped as "add federation peer". This indicates a database problem (constraint, connection, schema) rather than a validation problem. Check the wrapped driver error for the root cause.

Source

Thrown at internal/storage/issueops/federation.go:49

// should already be encrypted by the caller; pass nil for no password.
func AddFederationPeerInTx(ctx context.Context, tx *sql.Tx, peer *storage.FederationPeer, encryptedPwd []byte) error {
	if err := ValidatePeerName(peer.Name); err != nil {
		return fmt.Errorf("invalid peer name: %w", err)
	}

	_, err := tx.ExecContext(ctx, `
		INSERT INTO federation_peers (name, remote_url, username, password_encrypted, sovereignty)
		VALUES (?, ?, ?, ?, ?)
		ON DUPLICATE KEY UPDATE
			remote_url = VALUES(remote_url),
			username = VALUES(username),
			password_encrypted = VALUES(password_encrypted),
			sovereignty = VALUES(sovereignty),
			updated_at = CURRENT_TIMESTAMP
	`, peer.Name, peer.RemoteURL, peer.Username, encryptedPwd, peer.Sovereignty)

	if err != nil {
		return fmt.Errorf("add federation peer: %w", err)
	}
	return nil
}

// FederationPeerRow holds raw database fields for a federation peer.
// The caller is responsible for decrypting EncryptedPwd.
type FederationPeerRow struct {
	Peer         storage.FederationPeer
	EncryptedPwd []byte
}

// 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

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped driver error to identify the root cause (missing table, deadlock, connection refused)
  2. Run pending schema migrations so federation_peers exists with the expected columns
  3. Retry the transaction if the error was transient (connection reset, deadlock)
  4. Ensure the transaction is still open and not already aborted/rolled back

Example fix

// before
err := issueops.AddFederationPeerInTx(ctx, tx, peer, pwd) // fails: unknown table federation_peers
// after
if err := migrate(ctx, db); err != nil { // ensure schema up to date
    return err
}
tx, _ := db.BeginTx(ctx, nil)
err := issueops.AddFederationPeerInTx(ctx, tx, peer, pwd)
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure schema is current before writing
if err := migrate(ctx, db); err != nil {
    return err
}

Try / catch

err := issueops.AddFederationPeerInTx(ctx, tx, peer, pwd)
if err != nil {
    var driverErr *mysqldriver.MySQLError
    if errors.As(err, &driverErr) {
        log.Printf("upsert peer %s failed: code=%d msg=%s", peer.Name, driverErr.Number, driverErr.Message)
    }
    return fmt.Errorf("add federation peer %s: %w", peer.Name, err)
}

Prevention

When it happens

Trigger: tx.ExecContext on the federation_peers upsert returns a driver error: table missing, schema mismatch, connection dropped, or constraint violation not caught by name validation.

Common situations: Running against an old database where the federation_peers table doesn't exist yet (needs migration); connection to the Dolt server dropped mid-transaction; another process dropped/locked the table.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


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