netbirdio/netbird · error
invalid pin: must be exactly 6 digits
Error message
invalid pin: must be exactly 6 digits
What it means
Returned by ExposeServiceRequest.Validate when the peer-initiated expose request sets a Pin that does not match ^\d{6}$ - exactly six ASCII digits. The pin becomes a PINAuthConfig on the resulting service, and the six-digit shape is a product contract for the expose flow. Note the L4 guard earlier in the same function already rejected any pin for tcp/udp/tls modes, so this error can only surface for http-mode expose requests.
Source
Thrown at management/internals/modules/reverseproxy/service/service.go:1494
if r.Port == 0 {
return fmt.Errorf("port must be between 1 and 65535, got %d", r.Port)
}
switch r.Mode {
case ModeHTTP, ModeTCP, ModeUDP, ModeTLS:
default:
return fmt.Errorf("unsupported mode %q", r.Mode)
}
if IsL4Protocol(r.Mode) {
if r.Pin != "" || r.Password != "" || len(r.UserGroups) > 0 {
return fmt.Errorf("authentication is not supported for %s mode", r.Mode)
}
}
if r.Pin != "" && !pinRegexp.MatchString(r.Pin) {
return errors.New("invalid pin: must be exactly 6 digits")
}
for _, g := range r.UserGroups {
if g == "" {
return errors.New("user group name cannot be empty")
}
}
if r.NamePrefix != "" && !validNamePrefix.MatchString(r.NamePrefix) {
return fmt.Errorf("invalid name prefix %q: must be lowercase alphanumeric with optional hyphens, 1-32 characters", r.NamePrefix)
}
return nil
}
// ToService builds a Service from the expose request.
func (r *ExposeServiceRequest) ToService(accountID, peerID, serviceName string) *Service {
svc := &Service{View on GitHub (pinned to 93e97f4bf1)
Solutions
- Send the pin as a string of exactly six digits, e.g. "435678".
- Make the client field a string type and validate with ^\d{6}$ before submitting; never a number.
- Leave pin empty to skip pin auth for the exposed service (other auth like password/user_groups is still available).
Example fix
// before
req := ExposeServiceRequest{ Mode: "http", Port: 8080, Pin: "1234" }
// after
req := ExposeServiceRequest{ Mode: "http", Port: 8080, Pin: "435678" } Defensive patterns
Strategy: validation
Validate before calling
var pinRe = regexp.MustCompile(`^\d{6}$`)
func checkExposePin(mode, pin string) error {
if pin == "" {
return nil
}
if pinRe.MatchString(pin) != true {
return errors.New("pin must be exactly 6 digits")
}
return nil
} Type guard
func isValidExposePin(pin string) bool {
return pin == "" || pinRe.MatchString(pin)
} Try / catch
if err := req.Validate(); err != nil {
if strings.Contains(err.Error(), "invalid pin") {
return respondBadRequest(errors.New("send pin as a 6-digit string, e.g. \"435678\""))
}
return respondBadRequest(err)
} Prevention
- Type the pin as string end to end - never int, or leading zeros disappear.
- Validate with ^\d{6}$ in the client before the API call (CLI arg, form field, JSON schema).
- Do not set a pin at all for tcp/udp/tls expose requests - the L4 guard rejects any pin first.
When it happens
Trigger: Calling the expose API (peer 'netbird expose' path) with pin "1234" (4 digits), "1234567" (7), "12 456" (space), "abcdef", or a pin passed as a JSON number where the serializer dropped leading zeros (012345 became "12345").
Common situations: Porting 4-digit PIN conventions from other products. Client-side types using int for the pin, losing leading zeros and allowing non-6-digit values. Copy-paste with a trailing newline or whitespace inside the pin string.
Related errors
- auth is not supported for TCP/UDP services
- auth is not supported for TLS services
- user group name cannot be empty
- service name is required
- service name exceeds maximum length of 255 characters
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/d284d5c27bc80c89.
Report an issue: GitHub.