{"record":{"id":"19ab9925a3915f31","repo":"juanfont/headscale","slug":"username-cannot-contain-more-than-one","errorCode":null,"errorMessage":"username cannot contain more than one '@'","messagePattern":"username cannot contain more than one '@'","errorType":"validation","errorClass":"util.ErrUsernameTooManyAt","httpStatus":null,"severity":"warning","filePath":"hscontrol/util/dns.go","lineNumber":32,"sourceCode":")\n\nconst (\n\tByteSize          = 8\n\tipv4AddressLength = 32\n\tipv6AddressLength = 128\n\n\t// LabelHostnameLength is the maximum length for a DNS label,\n\t// value related to RFC 1123 and 952.\n\tLabelHostnameLength = 63\n)\n\n// DNS validation errors. Hostname-side validation lives on\n// `tailscale.com/util/dnsname` and [state.NodeStore] collision handling; only\n// the username-side errors stay in this package.\nvar (\n\tErrUsernameTooShort        = errors.New(\"username must be at least 2 characters long\")\n\tErrUsernameMustStartLetter = errors.New(\"username must start with a letter\")\n\tErrUsernameTooManyAt       = errors.New(\"username cannot contain more than one '@'\")\n\tErrUsernameInvalidChar     = errors.New(\"username contains invalid character\")\n)\n\n// ValidateUsername checks if a username is valid.\n// It must be at least 2 characters long, start with a letter, and contain\n// only letters, numbers, hyphens, dots, and underscores.\n// It cannot contain more than one '@'.\n// It cannot contain invalid characters.\nfunc ValidateUsername(username string) error {\n\t// Ensure the username meets the minimum length requirement\n\tif len(username) < 2 {\n\t\treturn ErrUsernameTooShort\n\t}\n\n\t// Ensure the username starts with a letter\n\tif !unicode.IsLetter(rune(username[0])) {\n\t\treturn ErrUsernameMustStartLetter\n\t}","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/util/dns.go#L14-L50","documentation":"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.","triggerScenarios":"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.","commonSituations":"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 '@'.","solutions":["Use at most one '@' — strip the duplicated domain part before creating the user","For OIDC, pick a claim that is a bare username, or normalize the claim value in your IdP","Pre-validate with util.ValidateUsername in automation"],"exampleFix":"// before\nusername := email + \"@example.com\" // user@example.com@example.com\n\n// after\nusername := localPart(email) // user","handlingStrategy":"validation","validationCode":"if strings.Count(username, \"@\") > 1 {\n\treturn fmt.Errorf(\"username %q has multiple '@': %w\", username, util.ErrUsernameTooManyAt)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never append '@domain' to a value that may already contain '@'","Normalize OIDC claims (strip domain) before user creation"],"tags":["validation","username","dns","users"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}