juanfont/headscale · warning · util.ErrUsernameTooManyAt

username cannot contain more than one '@'

Error message

username cannot contain more than one '@'

What it means

ErrUsernameTooManyAt (hscontrol/util/dns.go:32) is returned by ValidateUsername when the username contains more than one '@' character. Exactly one '@' is tolerated so that email-style names (user@domain) can be used, but a second '@' would break label derivation for MagicDNS and cannot be represented as a valid identity name.

Source

Thrown at hscontrol/util/dns.go:32

)

const (
	ByteSize          = 8
	ipv4AddressLength = 32
	ipv6AddressLength = 128

	// LabelHostnameLength is the maximum length for a DNS label,
	// value related to RFC 1123 and 952.
	LabelHostnameLength = 63
)

// DNS validation errors. Hostname-side validation lives on
// `tailscale.com/util/dnsname` and [state.NodeStore] collision handling; only
// the username-side errors stay in this package.
var (
	ErrUsernameTooShort        = errors.New("username must be at least 2 characters long")
	ErrUsernameMustStartLetter = errors.New("username must start with a letter")
	ErrUsernameTooManyAt       = errors.New("username cannot contain more than one '@'")
	ErrUsernameInvalidChar     = errors.New("username contains invalid character")
)

// ValidateUsername checks if a username is valid.
// It must be at least 2 characters long, start with a letter, and contain
// only letters, numbers, hyphens, dots, and underscores.
// It cannot contain more than one '@'.
// It cannot contain invalid characters.
func ValidateUsername(username string) error {
	// Ensure the username meets the minimum length requirement
	if len(username) < 2 {
		return ErrUsernameTooShort
	}

	// Ensure the username starts with a letter
	if !unicode.IsLetter(rune(username[0])) {
		return ErrUsernameMustStartLetter
	}

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Use at most one '@' — strip the duplicated domain part before creating the user
  2. For OIDC, pick a claim that is a bare username, or normalize the claim value in your IdP
  3. Pre-validate with util.ValidateUsername in automation

Example fix

// before
username := email + "@example.com" // user@example.com@example.com

// after
username := localPart(email) // user
Defensive patterns

Strategy: validation

Validate before calling

if strings.Count(username, "@") > 1 {
	return fmt.Errorf("username %q has multiple '@': %w", username, util.ErrUsernameTooManyAt)
}

Prevention

When it happens

Trigger: Calling util.ValidateUsername with strings like "a@b@c" or "user@@example.com"; typically reached via user creation (CLI/API) or OIDC claim mapping that concatenates a domain onto an already-qualified name.

Common situations: OIDC providers returning 'name@tenant@domain' style claims; provisioning scripts appending '@company.com' to a username that already includes '@company.com'; copy/paste artifacts adding a stray '@'.

Related errors


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/19ab9925a3915f31. Report an issue: GitHub.