fatedier/frp · error
%s
Error message
%s
What it means
Returned by nathole.PreCheck (pkg/nathole/nathole.go) when the NatHoleResp arrives successfully but carries a non-empty Error field — i.e. frps (the nathole controller) explicitly rejected the precheck. The string is the server's own message, commonly 'xtcp server for [name] doesn't exist' or 'xtcp connection of [name] auth failed' from the controller's visitor-message handler.
Source
Thrown at pkg/nathole/nathole.go:112
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 {
return nil, fmt.Errorf("discover error: %v", err)
}
if len(addrs) < 2 {
return nil, fmt.Errorf("discover error: not enough addresses")
}
localIPs, _ := ListLocalIPsForNatHole(10)
natFeature, err := ClassifyNATFeature(addrs, localIPs)
if err != nil {View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Read the embedded server message — it names the exact server-side cause (missing proxy vs auth failure)
- If 'doesn't exist': start the frpc that owns the xtcp proxy, confirm the name matches serverName
- If 'auth failed': make the visitor's sk identical to the client's xtcp sk
- Retry PreCheck after fixing config; call it before Prepare to fail fast
Defensive patterns
Strategy: try-catch
Try / catch
if err := nathole.PreCheck(ctx, tp, proxyName, timeout); err != nil {
switch {
case strings.Contains(err.Error(), "doesn't exist"):
// wait for client registration, fix serverName
case strings.Contains(err.Error(), "auth failed"):
// fix sk
default:
return err // transport-level, see error 292
}
} Prevention
- PreCheck errors carry the server's reason verbatim — branch on it
- Fix config (serverName/sk) before retrying
- PreCheck cheaply gates the expensive Prepare step
When it happens
Trigger: nathole.PreCheck succeeds at transport level, but the controller rejects the request: target xtcp proxy not registered (client offline or name mismatch), visitor sk mismatch, or duplicate registration.
Common situations: Visitor references an xtcp proxy whose client is not connected; serverName typo; sk mismatch between visitor and client; client still registering when the visitor fires.
Related errors
- proxy [%s] is repeated
- xtcp server for [%s] doesn't exist
- xtcp connection of [%s] auth failed
- no external address found
- not enough addresses
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/b73df926b3286fe4.
Report an issue: GitHub.