fatedier/frp · error

not enough addresses

Error message

not enough addresses

What it means

Thrown by ClassifyNATFeature in pkg/nathole/classify.go when the addresses slice has one or zero entries. NAT classification compares the IP and port observed from at least two different STUN observations to infer endpoint-independent vs address-dependent mapping behavior; with a single observation there is nothing to compare, so classification is impossible.

Source

Thrown at pkg/nathole/classify.go:44

	HardNAT = "HardNAT"

	BehaviorNoChange    = "BehaviorNoChange"
	BehaviorIPChanged   = "BehaviorIPChanged"
	BehaviorPortChanged = "BehaviorPortChanged"
	BehaviorBothChanged = "BehaviorBothChanged"
)

type NatFeature struct {
	NatType            string
	Behavior           string
	PortsDifference    int
	RegularPortsChange bool
	PublicNetwork      bool
}

func ClassifyNATFeature(addresses []string, localIPs []string) (*NatFeature, error) {
	if len(addresses) <= 1 {
		return nil, fmt.Errorf("not enough addresses")
	}
	natFeature := &NatFeature{}
	ipChanged := false
	portChanged := false

	var baseIP, basePort string
	var portMax, portMin int
	for _, addr := range addresses {
		ip, port, err := net.SplitHostPort(addr)
		if err != nil {
			return nil, err
		}
		portNum, err := strconv.Atoi(port)
		if err != nil {
			return nil, err
		}
		if slices.Contains(localIPs, ip) {
			natFeature.PublicNetwork = true

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Ensure Discover(stunServers, ...) is given at least two reachable STUN servers and returns >= 2 addresses before classifying
  2. Check natFeature/stun server reachability: verify natHoleSTUNServer in frps.toml points to working STUN endpoints
  3. Retry discovery — transient UDP loss can drop one observation
  4. If calling ClassifyNATFeature yourself, guard with len(addresses) < 2 and skip classification

Example fix

// before
feature, err := ClassifyNATFeature(addrs, localIPs)

// after
if len(addrs) < 2 {
    return nil, fmt.Errorf("need at least 2 discovered addresses, got %d", len(addrs))
}
feature, err := ClassifyNATFeature(addrs, localIPs)
Defensive patterns

Strategy: validation

Validate before calling

if len(addresses) < 2 {
    return nil, fmt.Errorf("classification needs >=2 addresses, got %d", len(addresses))
}

Try / catch

feature, err := nathole.ClassifyNATFeature(addrs, localIPs)
if err != nil {
    if strings.Contains(err.Error(), "not enough addresses") {
        // re-run discovery with more STUN servers before retrying
    }
    return err
}

Prevention

When it happens

Trigger: Calling ClassifyNATFeature(addresses, localIPs) with len(addresses) <= 1. In frp this happens when Discover() returned only one mapped address because only one STUN server responded, or when a caller passes a single hard-coded address instead of the multi-server discovery result.

Common situations: STUN servers misconfigured or unreachable so discovery yields one address; firewalls blocking UDP to all but one STUN endpoint; custom code that calls ClassifyNATFeature directly without first checking the discovery result length; symmetric NAT environments where the second STUN query fails.

Related errors


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/1dffbc3cddf834a8. Report an issue: GitHub.