{"record":{"id":"a16bad14629266a7","repo":"gastownhall/beads","slug":"invalid-peer-name-w-a16bad","errorCode":null,"errorMessage":"invalid peer name: %w","messagePattern":"invalid peer name: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/storage/issueops/federation.go","lineNumber":34,"sourceCode":"// ValidatePeerName checks that a peer name is safe for use as a Dolt remote name.\nfunc ValidatePeerName(name string) error {\n\tif name == \"\" {\n\t\treturn fmt.Errorf(\"peer name cannot be empty\")\n\t}\n\tif len(name) > 64 {\n\t\treturn fmt.Errorf(\"peer name too long (max 64 characters)\")\n\t}\n\tif !validPeerNameRegex.MatchString(name) {\n\t\treturn fmt.Errorf(\"peer name must start with a letter and contain only alphanumeric characters, hyphens, and underscores\")\n\t}\n\treturn nil\n}\n\n// AddFederationPeerInTx upserts a federation peer record. The encryptedPwd\n// 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}","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/federation.go#L16-L52","documentation":"AddFederationPeerInTx validates the peer's Name field via ValidatePeerName before inserting, and wraps any validation failure with \"invalid peer name\". This is a pure input-validation error: the peer record was rejected before touching the database. Fix the peer name in your FederationPeer struct and retry.","triggerScenarios":"Calling AddFederationPeerInTx with a FederationPeer whose Name is empty, contains whitespace/illegal characters, or otherwise fails ValidatePeerName.","commonSituations":"Constructing a FederationPeer from unparsed config, CLI flags, or YAML where the name field was left blank or contains spaces; syncing peer metadata from another instance with legacy names.","solutions":["Validate the peer name with issueops.ValidatePeerName(peer.Name) before calling AddFederationPeerInTx and show the underlying reason to the user","Inspect the wrapped error (%w) for the specific rule violated (empty, invalid characters, too long) and correct peer.Name","If names come from user input or config, trim and normalize them at load time"],"exampleFix":"// before\npeer := &storage.FederationPeer{Name: cfg.PeerName} // may be \"\" or \"my peer\"\nerr := issueops.AddFederationPeerInTx(ctx, tx, peer, nil)\n// after\nname := strings.TrimSpace(cfg.PeerName)\nif err := issueops.ValidatePeerName(name); err != nil {\n    return fmt.Errorf(\"bad --peer-name: %w\", err)\n}\npeer := &storage.FederationPeer{Name: name}\nerr := issueops.AddFederationPeerInTx(ctx, tx, peer, nil)","handlingStrategy":"validation","validationCode":"if err := issueops.ValidatePeerName(peer.Name); err != nil {\n    return fmt.Errorf(\"peer name %q invalid: %w\", peer.Name, err)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always call ValidatePeerName before AddFederationPeerInTx","Trim and normalize names sourced from CLI flags, env vars, or config files","Reject empty names at config-load time, not at DB-write time"],"tags":["validation","federation","go"],"backgroundTag":"invalid-peer-name","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}