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

  1. Read the embedded server message — it names the exact server-side cause (missing proxy vs auth failure)
  2. If 'doesn't exist': start the frpc that owns the xtcp proxy, confirm the name matches serverName
  3. If 'auth failed': make the visitor's sk identical to the client's xtcp sk
  4. 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

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


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