fatedier/frp · error
wait detect message error: %v
Error message
wait detect message error: %v
What it means
MakeHole failed because waitDetectMessage returned an error on the single listening UDP socket: the peer's detect packet could not be received before the deadline (ReadFromUDP error, deadline exceeded, or an invalid SID/role match). This is the actual hole-punch packet exchange failing after the coordination succeeded.
Source
Thrown at pkg/nathole/nathole.go:255
}
}
if m.DetectBehavior.SendRandomPorts > 0 {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
for i := range listenConns {
go sendSidMessageToRandomPorts(ctx, listenConns[i], m.CandidateAddrs, m.DetectBehavior.SendRandomPorts, sendToRangePortsFunc)
}
}
timeout := 5 * time.Second
if m.DetectBehavior.ReadTimeoutMs > 0 {
timeout = time.Duration(m.DetectBehavior.ReadTimeoutMs) * time.Millisecond
}
if len(listenConns) == 1 {
raddr, err := waitDetectMessage(ctx, listenConns[0], m.Sid, key, timeout, m.DetectBehavior.Role)
if err != nil {
return nil, nil, fmt.Errorf("wait detect message error: %v", err)
}
return listenConns[0], raddr, nil
}
type result struct {
lConn *net.UDPConn
raddr *net.UDPAddr
}
resultCh := make(chan result)
for _, conn := range listenConns {
go func(lConn *net.UDPConn) {
addr, err := waitDetectMessage(ctx, lConn, m.Sid, key, timeout, m.DetectBehavior.Role)
if err != nil {
lConn.Close()
return
}
select {
case resultCh <- result{lConn: lConn, raddr: addr}:View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Verify both sides use the same auth token so the detect messages decrypt and the SID matches
- Retry the visit — hole punching is inherently racy and often succeeds on later attempts
- If it consistently fails, one endpoint is likely behind a symmetric NAT: fall back to relaying through frps (do not rely on P2P)
- Check DetectBehavior.ReadTimeoutMs in the server response; raise it server-side if the network is slow
Defensive patterns
Strategy: retry
Try / catch
conn, addr, err := nathole.MakeHole(ctx, l, resp, key)
if err != nil && strings.Contains(err.Error(), "wait detect message") {
// UDP punch is racy: retry N times with backoff before giving up
for i := 0; i < 3 && err != nil; i++ {
time.Sleep(time.Duration(i+1) * 500 * time.Millisecond)
conn, addr, err = retryMakeHole()
}
} Prevention
- Verify auth token parity before P2P attempts so SID decryption succeeds
- Increase server-side DetectBehavior.ReadTimeoutMs on slow links
- Assume symmetric NATs exist: always wire a relay fallback path
When it happens
Trigger: MakeHole with exactly one listenConn and waitDetectMessage errors: UDP read deadline expires, the socket errors, or every incoming packet is discarded because the decrypted SID does not match m.Sid for the expected role.
Common situations: NAT rewrites the source port so detect packets arrive from an unexpected address; symmetric NAT on either side blocks the hole; packet loss or heavy UDP filtering; wrong key (auth token) making SID decryption fail so valid packets are dropped.
Related errors
- wait detect message timeout
- open tunnel timeout
- dial quic error: %v
- natHoleRespMsg get error info: %s
- natHoleRespMsg get empty candidate addresses
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/80d9bdc88c4ad05c.
Report an issue: GitHub.