{"record":{"id":"bb86e35dbf978025","repo":"gastownhall/beads","slug":"invalid-peer-name-w","errorCode":null,"errorMessage":"invalid peer name: %w","messagePattern":"invalid peer name: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dolt/credentials.go","lineNumber":275,"sourceCode":"\ts.mu.RUnlock()\n\tif key == nil {\n\t\treturn \"\", fmt.Errorf(\"credential encryption key not initialized\")\n\t}\n\treturn decryptWithKey(encrypted, key)\n}\n\n// AddFederationPeer adds or updates a federation peer with credentials.\n// This stores credentials in the database and also adds the Dolt remote.\nfunc (s *DoltStore) AddFederationPeer(ctx context.Context, peer *storage.FederationPeer) error {\n\treturn s.withCircuitWrite(ctx, func(ctx context.Context) error {\n\t\treturn s.addFederationPeer(ctx, peer)\n\t})\n}\n\nfunc (s *DoltStore) addFederationPeer(ctx context.Context, peer *storage.FederationPeer) error {\n\t// Validate peer name\n\tif err := validatePeerName(peer.Name); err != nil {\n\t\treturn fmt.Errorf(\"invalid peer name: %w\", err)\n\t}\n\n\t// Encrypt password before storing\n\tvar encryptedPwd []byte\n\tvar err error\n\tif peer.Password != \"\" {\n\t\tif err := s.ensureCredentialKey(ctx); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to initialize credential key: %w\", err)\n\t\t}\n\t\tencryptedPwd, err = s.encryptPassword(peer.Password)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to encrypt password: %w\", err)\n\t\t}\n\t}\n\n\t// Upsert the peer credentials\n\t_, err = s.execContext(ctx, `\n\t\tINSERT INTO federation_peers (name, remote_url, username, password_encrypted, sovereignty)","sourceCodeStart":257,"sourceCodeEnd":293,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/credentials.go#L257-L293","documentation":"addFederationPeer validates the peer name before storing credentials; this wraps any validatePeerName failure. A valid peer name must be non-empty, at most 64 characters, start with a letter, and contain only alphanumerics, hyphens, and underscores (regex ^[a-zA-Z][a-zA-Z0-9_-]*$). The name is later used as a Dolt remote name, so unsafe characters are rejected up front.","triggerScenarios":"Calling AddFederationPeer with a peer.Name that is empty, longer than 64 chars, starts with a digit/underscore/hyphen, or contains spaces, dots, slashes, or other special characters.","commonSituations":"Deriving peer names from hostnames or URLs containing dots/slashes (e.g. \"team.example.com\", \"org/repo\"); user-supplied names from config files or CLI flags without pre-validation; accidentally passing an empty struct.","solutions":["Fix the peer name to match ^[a-zA-Z][a-zA-Z0-9_-]*$ (max 64 chars, starts with a letter).","Sanitize derived names: replace '.', '/', ':' etc. with '-' and strip leading non-letters before calling AddFederationPeer.","Call validatePeerName-equivalent checks (regex) in your own config loading to fail early with a clear message."],"exampleFix":"// before\npeer.Name = \"team.example.com\"\n_ = store.AddFederationPeer(ctx, peer)\n// after\npeer.Name = \"team-example-com\"\n_ = store.AddFederationPeer(ctx, peer)","handlingStrategy":"validation","validationCode":"var peerNameRe = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_-]*$`)\nif peer.Name == \"\" || len(peer.Name) > 64 || !peerNameRe.MatchString(peer.Name) {\n    return fmt.Errorf(\"peer name %q is invalid\", peer.Name)\n}","typeGuard":null,"tryCatchPattern":"if err := store.AddFederationPeer(ctx, peer); err != nil && strings.HasPrefix(err.Error(), \"invalid peer name\") {\n    return fmt.Errorf(\"fix peer name %q: %w\", peer.Name, err)\n}","preventionTips":["Sanitize hostnames/URLs into remote-safe names before storing","Validate names at config-load time, not just at write time","Keep names short (<=64 chars) and alphanumeric"],"tags":["go","validation","federation","naming"],"backgroundTag":"invalid-peer-name","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}