fatedier/frp · error
xtcp connection of [%s] auth failed
Error message
xtcp connection of [%s] auth failed
What it means
Thrown in the nathole Controller's visitor-message handler (pkg/nathole/controller.go) when the visitor's SignKey does not match the expected auth key derived from the client's secret key and timestamp (util.GetAuthKey(clientCfg.sk, m.Timestamp), compared in constant time). The xtcp visitor must share the same secret key (sk) as the xtcp proxy's client; otherwise authentication fails and the error is returned to the visitor in the NatHoleResp.
Source
Thrown at pkg/nathole/controller.go:190
sid: sid,
visitorMsg: m,
visitorTransporter: transporter,
notifyCh: make(chan struct{}, 1),
}
var (
clientCfg *ClientCfg
ok bool
)
err := func() error {
c.mu.Lock()
defer c.mu.Unlock()
clientCfg, ok = c.clientCfgs[m.ProxyName]
if !ok {
return fmt.Errorf("xtcp server for [%s] doesn't exist", m.ProxyName)
}
if !util.ConstantTimeEqString(m.SignKey, util.GetAuthKey(clientCfg.sk, m.Timestamp)) {
return fmt.Errorf("xtcp connection of [%s] auth failed", m.ProxyName)
}
c.sessions[sid] = session
return nil
}()
if err != nil {
log.Warnf("handle visitorMsg error: %v", err)
_ = transporter.Send(c.GenNatHoleResponse(m.TransactionID, nil, err.Error()))
return
}
log.Tracef("handle visitor message, sid [%s], server name: %s", sid, m.ProxyName)
defer func() {
c.mu.Lock()
defer c.mu.Unlock()
delete(c.sessions, sid)
}()
if err := errors.PanicToError(func() {View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Set the same sk value in the visitor config as in the client's xtcp proxy config
- After rotating secrets, update and restart both client and visitor
- Check for trailing whitespace/newlines when pasting sk into config files
- Verify the timestamp is not wildly off (clock skew affects key derivation inputs)
Example fix
# before (visitor) [[visitors]] name = "p2p-v" type = "xtcp" serverName = "p2p" sk = "old-secret" # after [[visitors]] name = "p2p-v" type = "xtcp" serverName = "p2p" sk = "same-secret-as-client"
Defensive patterns
Strategy: validation
Try / catch
if err := nathole.PreCheck(ctx, tp, proxyName, timeout); err != nil {
if strings.Contains(err.Error(), "auth failed") {
// fix sk in visitor config to match client, then retry
}
} Prevention
- Keep sk identical on client xtcp proxy and visitor
- Rotate secrets on both sides together
- Watch for whitespace when pasting sk values
When it happens
Trigger: A NatHoleVisitor message whose ProxyName resolves to a registered client, but whose sk differs from the client's configured secret. Common with copy-pasted or rotated sk values, or when the visitor config omits sk entirely (falling back to a different default).
Common situations: sk in the visitor block does not match sk on the client's xtcp proxy; the secret was rotated on one side only; sk omitted from the visitor config so an empty/default value is signed with.
Related errors
- proxy [%s] is repeated
- xtcp server for [%s] doesn't exist
- %s
- no external address found
- not enough addresses
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/b0a87e55134e6fbd.
Report an issue: GitHub.