fatedier/frp · error
get natHoleRespMsg error: %v
Error message
get natHoleRespMsg error: %v
What it means
Returned by nathole.PreCheck (pkg/nathole/nathole.go) when the underlying transporter.Do round-trip fails while sending a NatHoleVisitor{PreCheck:true} and waiting for the matching NatHoleResp. This is the transport-level failure (send error, timeout, connection closed) — distinct from a server-side rejection, which arrives as a successful response carrying an Error string.
Source
Thrown at pkg/nathole/nathole.go:103
// PreCheck is used to check if the proxy is ready for penetration.
// Call this function before calling Prepare to avoid unnecessary preparation work.
func PreCheck(
ctx context.Context, transporter transport.MessageTransporter,
proxyName string, timeout time.Duration,
) error {
timeoutCtx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
var natHoleRespMsg *msg.NatHoleResp
transactionID := NewTransactionID()
m, err := transporter.Do(timeoutCtx, &msg.NatHoleVisitor{
TransactionID: transactionID,
ProxyName: proxyName,
PreCheck: true,
}, transactionID, msg.TypeNameNatHoleResp)
if err != nil {
return fmt.Errorf("get natHoleRespMsg error: %v", err)
}
mm, ok := m.(*msg.NatHoleResp)
if !ok {
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 {View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Inspect the wrapped error — a context deadline means raise the timeout or fix latency; a connection error means re-establish the transport first
- Retry PreCheck after reconnecting the transport
- Verify frps is up and the nathole controller port is reachable from the visitor
- Only call Prepare after PreCheck succeeds to avoid wasted discovery work
Example fix
// before
err := nathole.PreCheck(ctx, tp, proxyName, 500*time.Millisecond)
// after
err := nathole.PreCheck(ctx, tp, proxyName, 3*time.Second)
if err != nil {
// reconnect transport and retry once before giving up
} Defensive patterns
Strategy: retry
Try / catch
if err := nathole.PreCheck(ctx, tp, proxyName, 3*time.Second); err != nil {
if errors.Is(err, context.DeadlineExceeded) || strings.Contains(err.Error(), "get natHoleRespMsg error") {
// reconnect transport and retry once before surfacing to the user
}
} Prevention
- Call PreCheck with a generous timeout on slow links
- Ensure the transport to frps is healthy before prechecking
- Always PreCheck before Prepare to avoid wasted STUN work
When it happens
Trigger: nathole.PreCheck(ctx, transporter, proxyName, timeout) where transporter.Do errors: the control/UDP connection to frps is broken, the timeout context fires before the response arrives, or the message transport was already closed.
Common situations: frps unreachable or restarted mid-precheck; timeout passed to PreCheck too short for a slow link; visitor's connection to the nathole controller dropped (UDP); network flapping between visitor and server.
Related errors
- wait response from stun server timeout
- no external address found
- discover error: %v
- resolve local udp addr error: %v
- listen local udp addr error: %v
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/3380b2d3cf0df5cb.
Report an issue: GitHub.