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

  1. Rename the peer to start with a letter and use only alphanumerics, hyphens, and underscores: `remote01`, `backup-remote`, `upstream_main`
  2. Sanitize generated names in scripts: replace invalid characters with `-` and prefix a letter if it starts with a digit
  3. 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

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


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