fatedier/frp · error

discover error: not enough addresses

Error message

discover error: not enough addresses

What it means

Returned by nathole.Prepare (pkg/nathole/nathole.go) when Discover succeeded but returned fewer than two addresses. NAT classification needs mappings observed from at least two distinct STUN servers to detect IP/port behavior differences; with one address there is no basis for classification, so Prepare refuses to continue.

Source

Thrown at pkg/nathole/nathole.go:125

		return fmt.Errorf("get natHoleRespMsg error: invalid message type")
	}
	natHoleRespMsg = mm

	if natHoleRespMsg.Error != "" {
		return fmt.Errorf("%s", natHoleRespMsg.Error)
	}
	return nil
}

// Prepare is used to do some preparation work before penetration.
func Prepare(stunServers []string, opts PrepareOptions) (*PrepareResult, error) {
	// discover for Nat type
	addrs, localAddr, err := Discover(stunServers, "")
	if err != nil {
		return nil, fmt.Errorf("discover error: %v", err)
	}
	if len(addrs) < 2 {
		return nil, fmt.Errorf("discover error: not enough addresses")
	}

	localIPs, _ := ListLocalIPsForNatHole(10)
	natFeature, err := ClassifyNATFeature(addrs, localIPs)
	if err != nil {
		return nil, fmt.Errorf("classify nat feature error: %v", err)
	}

	laddr, err := net.ResolveUDPAddr("udp4", localAddr.String())
	if err != nil {
		return nil, fmt.Errorf("resolve local udp addr error: %v", err)
	}
	listenConn, err := net.ListenUDP("udp4", laddr)
	if err != nil {
		return nil, fmt.Errorf("listen local udp addr error: %v", err)
	}

	// Apply NAT traversal options

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Configure at least two (preferably three) working STUN servers for the nathole controller
  2. Retry Prepare — losing one of two UDP exchanges is common and often succeeds on retry
  3. Confirm each STUN server individually responds from the host running frpc
  4. If the environment only ever yields one mapping, fall back to stcp/relay

Example fix

# before (frps.toml)
natholeSTUNServer = ["stun1.example.com:3478"]

# after
natHoleSTUNServer = ["stun1.example.com:3478", "stun2.example.com:3478", "stun3.example.com:3478"]
Defensive patterns

Strategy: retry

Validate before calling

addrs, _, err := nathole.Discover(servers, "")
if err == nil && len(addrs) < 2 {
    // not enough observations for classification: retry or expand server list
}

Try / catch

res, err := nathole.Prepare(servers, opts)
if err != nil && strings.Contains(err.Error(), "not enough addresses") {
    // one STUN observation lost: retry once, then add more STUN servers
}

Prevention

When it happens

Trigger: nathole.Prepare where Discover returned exactly one address — one STUN server answered, the second timed out or returned no OTHER-ADDRESS to chain the second request from.

Common situations: Only one STUN server configured; one of several servers down; a NAT that drops the follow-up STUN request to the changed address; flaky UDP paths.

Related errors


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