fatedier/frp · error
classify client nat feature error: %v
Error message
classify client nat feature error: %v
What it means
Wraps a ClassifyNATFeature failure for the client side inside Controller.analysis (pkg/nathole/controller.go). analysis takes the NatHoleClient message's MappedAddrs (observed by the xtcp server side via STUN) and classifies its NAT; if classification fails — most commonly 'not enough addresses' when fewer than two mapped addresses were reported — this error aborts the whole hole-punch analysis.
Source
Thrown at pkg/nathole/controller.go:306
if session != nil {
sid = session.sid
}
return &msg.NatHoleResp{
TransactionID: transactionID,
Sid: sid,
Error: errInfo,
}
}
// analysis analyzes the NAT type and behavior of the visitor and client, then makes hole-punching decisions.
// return the response to the visitor and client.
func (c *Controller) analysis(session *Session) (*msg.NatHoleResp, *msg.NatHoleResp, error) {
cm := session.clientMsg
vm := session.visitorMsg
cNatFeature, err := ClassifyNATFeature(cm.MappedAddrs, parseIPs(cm.AssistedAddrs))
if err != nil {
return nil, nil, fmt.Errorf("classify client nat feature error: %v", err)
}
vNatFeature, err := ClassifyNATFeature(vm.MappedAddrs, parseIPs(vm.AssistedAddrs))
if err != nil {
return nil, nil, fmt.Errorf("classify visitor nat feature error: %v", err)
}
session.cNatFeature = cNatFeature
session.vNatFeature = vNatFeature
session.genAnalysisKey()
mode, index, cBehavior, vBehavior := c.analyzer.GetRecommandBehaviors(session.analysisKey, cNatFeature, vNatFeature)
session.recommandMode = mode
session.recommandIndex = index
session.cBehavior = cBehavior
session.vBehavior = vBehavior
timeoutMs := max(cBehavior.SendDelayMs, vBehavior.SendDelayMs) + 5000
if cBehavior.ListenRandomPorts > 0 || vBehavior.ListenRandomPorts > 0 {View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Fix STUN reachability from the client: configure multiple working STUN servers (natHoleAnalysisSTUNServer / natHoleSTUNServer in frps.toml)
- Confirm the frpc hosting the xtcp proxy and frps run compatible versions so MappedAddrs format matches
- Retry the visitor connection — transient UDP loss during discovery is common
- Check the wrapped error text: 'not enough addresses' points to discovery, a SplitHostPort error points to malformed data
Example fix
# before (frps.toml) [natholeController] # missing or single dead STUN server # after bindAddr = "0.0.0.0" natholeAnalysisSTUNServer = ["stun.easyvoip.com:3478", "stun.l.google.com:19302"]
Defensive patterns
Strategy: retry
Try / catch
resp, err := controller.Analysis(session) // wraps analysis()
if err != nil && strings.Contains(err.Error(), "classify client nat feature error") {
// client-side STUN discovery insufficient: have client re-run discovery / check STUN servers, retry visit
} Prevention
- Configure multiple healthy STUN servers for the nathole controller
- Keep frpc/frps versions aligned
- Treat one-off classification failures as retryable before debugging config
When it happens
Trigger: Controller.analysis(session) runs after both client and visitor messages arrive; cm.MappedAddrs has <= 1 entry, or one of its entries fails net.SplitHostPort / strconv.Atoi (malformed 'host:port' string), causing ClassifyNATFeature to return an error that gets wrapped here.
Common situations: The client's STUN discovery only reached one server (blocked UDP, dead STUN config); the client reported addresses in a non-host:port format due to a version mismatch; network middleboxes mangling discovery traffic.
Related errors
- not enough addresses
- classify visitor nat feature error: %v
- discover error: not enough addresses
- 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/0b6b0691a22e5c64.
Report an issue: GitHub.