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

  1. From the visitor machine, verify UDP reachability to at least two STUN servers (e.g. with a STUN test client)
  2. Configure healthy STUN servers on frps (natHoleAnalysisSTUNServer) so both sides gather multiple observations
  3. Retry the visit; discovery is best-effort over UDP
  4. 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

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


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