{"record":{"id":"bacf944941a047cd","repo":"gastownhall/beads","slug":"add-federation-peer-w","errorCode":null,"errorMessage":"add federation peer: %w","messagePattern":"add federation peer: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/federation.go","lineNumber":49,"sourceCode":"// should already be encrypted by the caller; pass nil for no password.\nfunc AddFederationPeerInTx(ctx context.Context, tx *sql.Tx, peer *storage.FederationPeer, encryptedPwd []byte) error {\n\tif err := ValidatePeerName(peer.Name); err != nil {\n\t\treturn fmt.Errorf(\"invalid peer name: %w\", err)\n\t}\n\n\t_, err := tx.ExecContext(ctx, `\n\t\tINSERT INTO federation_peers (name, remote_url, username, password_encrypted, sovereignty)\n\t\tVALUES (?, ?, ?, ?, ?)\n\t\tON DUPLICATE KEY UPDATE\n\t\t\tremote_url = VALUES(remote_url),\n\t\t\tusername = VALUES(username),\n\t\t\tpassword_encrypted = VALUES(password_encrypted),\n\t\t\tsovereignty = VALUES(sovereignty),\n\t\t\tupdated_at = CURRENT_TIMESTAMP\n\t`, peer.Name, peer.RemoteURL, peer.Username, encryptedPwd, peer.Sovereignty)\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"add federation peer: %w\", err)\n\t}\n\treturn nil\n}\n\n// FederationPeerRow holds raw database fields for a federation peer.\n// The caller is responsible for decrypting EncryptedPwd.\ntype FederationPeerRow struct {\n\tPeer         storage.FederationPeer\n\tEncryptedPwd []byte\n}\n\n// GetFederationPeerInTx retrieves a federation peer by name.\n// Returns storage.ErrNotFound (wrapped) if the peer does not exist.\nfunc GetFederationPeerInTx(ctx context.Context, tx *sql.Tx, name string) (*FederationPeerRow, error) {\n\tvar row FederationPeerRow\n\tvar lastSync sql.NullTime\n\tvar username sql.NullString\n","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/federation.go#L31-L67","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read the wrapped driver error to identify the root cause (missing table, deadlock, connection refused)","Run pending schema migrations so federation_peers exists with the expected columns","Retry the transaction if the error was transient (connection reset, deadlock)","Ensure the transaction is still open and not already aborted/rolled back"],"exampleFix":"// before\nerr := issueops.AddFederationPeerInTx(ctx, tx, peer, pwd) // fails: unknown table federation_peers\n// after\nif err := migrate(ctx, db); err != nil { // ensure schema up to date\n    return err\n}\ntx, _ := db.BeginTx(ctx, nil)\nerr := issueops.AddFederationPeerInTx(ctx, tx, peer, pwd)","handlingStrategy":"try-catch","validationCode":"// ensure schema is current before writing\nif err := migrate(ctx, db); err != nil {\n    return err\n}","typeGuard":null,"tryCatchPattern":"err := issueops.AddFederationPeerInTx(ctx, tx, peer, pwd)\nif err != nil {\n    var driverErr *mysqldriver.MySQLError\n    if errors.As(err, &driverErr) {\n        log.Printf(\"upsert peer %s failed: code=%d msg=%s\", peer.Name, driverErr.Number, driverErr.Message)\n    }\n    return fmt.Errorf(\"add federation peer %s: %w\", peer.Name, err)\n}","preventionTips":["Run migrations before any federation write","Keep transactions short and open them just before the call","Use errors.As to surface driver error codes for retry decisions"],"tags":["database","sql","federation","go"],"backgroundTag":"database-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}