fatedier/frp · error

xtcp server for [%s] doesn't exist

Error message

xtcp server for [%s] doesn't exist

What it means

Thrown in the nathole Controller's visitor-message handler (pkg/nathole/controller.go) when a NatHoleVisitor message arrives for a proxy name that has no registered xtcp client configuration. The controller looks up c.clientCfgs[m.ProxyName] under lock; a miss means no client-side xtcp server is currently registered under that name, so hole punching cannot proceed and the error is sent back to the visitor inside a NatHoleResp.

Source

Thrown at pkg/nathole/controller.go:187

	sid := c.GenSid()
	session := &Session{
		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)

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Set the visitor's serverName in frpc.toml to exactly the client's xtcp proxy name
  2. Verify the client owning the xtcp proxy is connected and its proxy registered successfully (check frps dashboard/logs)
  3. Retry from the visitor once the client is confirmed online
  4. Check for typos and whitespace differences in proxy names between the two configs

Example fix

# before (visitor config, wrong target)
[[visitors]]
name = "p2p-v"
type = "xtcp"
serverName = "p2p-others"

# after
[[visitors]]
name = "p2p-v"
type = "xtcp"
serverName = "p2p"   # must equal the client's xtcp proxy name
Defensive patterns

Strategy: validation

Try / catch

if err := nathole.PreCheck(ctx, tp, proxyName, timeout); err != nil {
    if strings.Contains(err.Error(), "doesn't exist") {
        // client not registered yet: verify serverName, wait for client, retry
    }
    return err
}

Prevention

When it happens

Trigger: A visitor sends a NatHoleVisitor message for proxyName that was never registered via ListenClient, or whose registration was removed (client disconnected / CloseClient called) before the visitor's request arrived.

Common situations: Visitor config's serverName does not match the client's xtcp proxy name; the frpc hosting the xtcp proxy is offline or has not yet registered when the visitor connects; the client disconnected between registration and the visitor's punch attempt.

Related errors


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