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 optionsView on GitHub (pinned to 6c8a8d0a97)
Solutions
- Configure at least two (preferably three) working STUN servers for the nathole controller
- Retry Prepare — losing one of two UDP exchanges is common and often succeeds on retry
- Confirm each STUN server individually responds from the host running frpc
- 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
- Configure >=3 STUN servers so losing one still yields 2 addresses
- Retry Prepare once before debugging
- Check per-server responsiveness from the frpc host
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
- not enough addresses
- classify client nat feature error: %v
- classify visitor nat feature error: %v
- wait response from stun server timeout
- no external address found
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/53b3111638877469.
Report an issue: GitHub.