gastownhall/beads · error
peer name must start with a letter and contain only alphanum
Error message
peer name must start with a letter and contain only alphanumeric characters, hyphens, and underscores
What it means
validatePeerName enforces ^[a-zA-Z][a-zA-Z0-9_-]*$ so the name is safe as a Dolt remote name (no slashes, dots, spaces, or leading digits/symbols). Names violating this pattern are rejected before remote/credential creation.
Source
Thrown at internal/storage/dolt/credentials.go:48
const awsResponseChecksumValidationEnv = "AWS_RESPONSE_CHECKSUM_VALIDATION"
// federationEnvMutex protects process-wide env vars from concurrent access.
// Environment variables are process-global, so we need to serialize federation operations.
var federationEnvMutex sync.Mutex
// validPeerNameRegex matches valid peer names (alphanumeric, hyphens, underscores)
var validPeerNameRegex = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_-]*$`)
// validatePeerName checks that a peer name is safe for use as a Dolt remote name
func validatePeerName(name string) error {
if name == "" {
return fmt.Errorf("peer name cannot be empty")
}
if len(name) > 64 {
return fmt.Errorf("peer name too long (max 64 characters)")
}
if !validPeerNameRegex.MatchString(name) {
return fmt.Errorf("peer name must start with a letter and contain only alphanumeric characters, hyphens, and underscores")
}
return nil
}
// initCredentialKey loads or generates the credential encryption key.
// The key file is stored in .beads/ (beadsDir), NOT in .beads/dolt/ (dbPath),
// to avoid creating ghost directories in shared-server mode (GH bd-cby).
// Falls back to the old dbPath location for transparent migration.
func (s *DoltStore) initCredentialKey(ctx context.Context) error {
if s.beadsDir == "" {
return nil // No filesystem path — credential encryption unavailable
}
keyPath := filepath.Join(s.beadsDir, credentialKeyFile)
// Try to load from new location (.beads/)
key, err := os.ReadFile(keyPath) //nolint:gosec // G304: keyPath is derived from trusted beadsDir, not user input
if err == nil && len(key) == 32 {View on GitHub (pinned to 71377f2769)
Solutions
- Rename the peer to start with a letter and use only alphanumerics, hyphens, and underscores: `remote01`, `backup-remote`, `upstream_main`
- Sanitize generated names in scripts: replace invalid characters with `-` and prefix a letter if it starts with a digit
- Quote shell arguments to avoid spaces splitting the name into separate args
Example fix
// before bd peer add 01_backup.remote # invalid characters // after bd peer add r01-backup-remote
Defensive patterns
Strategy: validation
Validate before calling
var peerNameRe = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_-]*$`)
func sanitizePeerName(name string) string {
name = strings.Map(func(r rune) rune {
if (r>='a'&&r<='z')||(r>='A'&&r<='Z')||(r>='0'&&r<='9')||r=='-'||r=='_' { return r }
return '-'
}, strings.ReplaceAll(name, " ", "-"))
if name != "" && name[0] >= '0' && name[0] <= '9' { name = "p" + name }
return name
} Prevention
- Start peer names with a letter; prefix numeric identifiers (e.g. `p01-remote`)
- Replace dots, slashes, and spaces with hyphens or underscores in generated names
- Quote shell arguments to prevent spaces from splitting the name
- Test derived names against ^[a-zA-Z][a-zA-Z0-9_-]*$ before use
When it happens
Trigger: addFederationPeer is given a name that starts with a digit/symbol or contains disallowed characters — e.g. `1st-peer`, `my peer`, `org/repo`, `peer.local`, or a URL used as the name.
Common situations: Using hostnames with dots, org/repo slashes, or names auto-derived from URLs; leading-digit numbering schemes (`01-remote`); spaces from unquoted shell arguments.
Related errors
- peer name cannot be empty
- peer name too long (max 64 characters)
- peer name cannot be empty
- peer name too long (max 64 characters)
- peer name must start with a letter and contain only alphanum
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/dcc20e36f05f893f.
Report an issue: GitHub.