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

  1. Send the pin as a string of exactly six digits, e.g. "435678".
  2. Make the client field a string type and validate with ^\d{6}$ before submitting; never a number.
  3. 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

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


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/d284d5c27bc80c89. Report an issue: GitHub.