fatedier/frp · error
classify visitor nat feature error: %v
Error message
classify visitor nat feature error: %v
What it means
Wraps a ClassifyNATFeature failure for the visitor side inside Controller.analysis (pkg/nathole/controller.go). After classifying the client's NAT successfully, analysis does the same for vm.MappedAddrs (the xtcp visitor's STUN-observed addresses); failure here — typically fewer than two mapped addresses or a malformed address — aborts hole-punch analysis with this error.
Source
Thrown at pkg/nathole/controller.go:311
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 {
timeoutMs += 30000
}
protocol := vm.Protocol
vResp := newNatHoleResponse(View on GitHub (pinned to 6c8a8d0a97)
Solutions
- From the visitor machine, verify UDP reachability to at least two STUN servers (e.g. with a STUN test client)
- Configure healthy STUN servers on frps (natHoleAnalysisSTUNServer) so both sides gather multiple observations
- Retry the visit; discovery is best-effort over UDP
- If it persists, xtcp hole punching may be impossible from that network — fall back to relay (stcp or a tcp proxy)
Defensive patterns
Strategy: retry
Try / catch
resp, err := controller.Analysis(session)
if err != nil && strings.Contains(err.Error(), "classify visitor nat feature error") {
// visitor's discovery was thin: retry the visit once, then check visitor-side UDP egress
} Prevention
- Verify visitor host can reach >=2 STUN servers over UDP
- Retry visits before treating failure as permanent
- Fall back to stcp relay when the visitor network starves discovery
When it happens
Trigger: Controller.analysis(session) where vm.MappedAddrs has <= 1 entry (only one STUN server responded for the visitor) or contains a string that fails host:port parsing. The visitor's discovery quality depends on its own network's UDP egress and the configured STUN servers.
Common situations: Visitor behind a restrictive firewall that blocks UDP to all but one STUN endpoint; visitor's frpc version reporting addresses differently; symmetric NAT dropping the second STUN exchange.
Related errors
- not enough addresses
- classify client nat feature error: %v
- discover error: not enough addresses
- xtcp server for [%s] doesn't exist
- wait response from stun server timeout
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/57c55eaf901b1250.
Report an issue: GitHub.