{"record":{"id":"4f299b2a5d0800c5","repo":"fatedier/frp","slug":"errnotunnelsession","errorCode":"ErrNoTunnelSession","errorMessage":"no tunnel session","messagePattern":"no tunnel session","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/visitor/xtcp.go","lineNumber":42,"sourceCode":"\t\"sync\"\n\t\"time\"\n\n\tlibio \"github.com/fatedier/golib/io\"\n\tfmux \"github.com/hashicorp/yamux\"\n\tquic \"github.com/quic-go/quic-go\"\n\t\"golang.org/x/time/rate\"\n\n\tv1 \"github.com/fatedier/frp/pkg/config/v1\"\n\t\"github.com/fatedier/frp/pkg/msg\"\n\t\"github.com/fatedier/frp/pkg/naming\"\n\t\"github.com/fatedier/frp/pkg/nathole\"\n\t\"github.com/fatedier/frp/pkg/transport\"\n\tnetpkg \"github.com/fatedier/frp/pkg/util/net\"\n\t\"github.com/fatedier/frp/pkg/util/util\"\n\t\"github.com/fatedier/frp/pkg/util/xlog\"\n)\n\nvar ErrNoTunnelSession = errors.New(\"no tunnel session\")\n\ntype XTCPVisitor struct {\n\t*BaseVisitor\n\tsession       TunnelSession\n\tstartTunnelCh chan struct{}\n\tretryLimiter  *rate.Limiter\n\tcancel        context.CancelFunc\n\n\tcfg *v1.XTCPVisitorConfig\n}\n\nfunc (sv *XTCPVisitor) Run() (err error) {\n\tsv.ctx, sv.cancel = context.WithCancel(sv.ctx)\n\n\tif sv.cfg.Protocol == \"kcp\" {\n\t\tsv.session = NewKCPTunnelSession()\n\t} else {\n\t\tsv.session = NewQUICTunnelSession(sv.clientCfg)","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/client/visitor/xtcp.go#L24-L60","documentation":"ErrNoTunnelSession is returned by KCPTunnelSession/QUICTunnelSession.OpenConn (xtcp.go:371,430) when the underlying session field is nil — i.e. OpenConn was called before Init() successfully completed or after Close() nulled the session. It signals the XTCP visitor has no NAT-hole tunnel established, not a network failure.","triggerScenarios":"An incoming connection arrives on the XTCP visitor's local listener while makeNatHole/Init has not finished (or failed); the session was closed concurrently (visitor shutdown, keepTunnelOpen retry cycle) and a late connection tries to open a stream.","commonSituations":"Connections hitting the visitor port immediately after startup before the first NAT hole punch completes; NAT hole punch failing (symmetric NAT) so the session never initializes while traffic still arrives; shutdown races where Close() and OpenConn contend.","solutions":["In custom visitors, ignore/reject connections during the pre-Init window instead of erroring loudly — the built-in keepTunnelOpen loop already skips ErrNoTunnelSession (xtcp.go:222)","Make the NAT hole punch succeed: verify both peers' NAT types and the frps nathole support (bindAddr for nathole on frps)","Enable keepTunnelOpen with retries so a session gets re-established automatically","Check that the visitor dialed the correct serverUDPPort for nathole discovery"],"exampleFix":"// before\nconn, err := sv.session.OpenConn(ctx)\nif err != nil { log.Error(err) } // no tunnel session spam\n\n// after\nconn, err := sv.session.OpenConn(ctx)\nif errors.Is(err, visitor.ErrNoTunnelSession) {\n    // tunnel not established yet; drop the client conn, retry will occur\n    clientConn.Close()\n    return\n}","handlingStrategy":"validation","validationCode":"// Mirror the built-in guard: only open conns when a session exists\ntype sessionReady interface{ Ready() bool }\n\nif s, ok := sv.session.(sessionReady); ok && !s.Ready() {\n    clientConn.Close() // no tunnel yet\n    return\n}","typeGuard":null,"tryCatchPattern":"conn, err := sv.session.OpenConn(ctx)\nif errors.Is(err, visitor.ErrNoTunnelSession) {\n    clientConn.Close()\n    return // hole punch pending/failed; keepTunnelOpen will retry\n}","preventionTips":["Copy the pattern at xtcp.go:222 — filter ErrNoTunnelSession out of retry/logging paths","Enable keepTunnelOpen + sane MaxRetriesAnHour so sessions self-heal","Expect this error during startup and after NAT punch failures; don't alert on it"],"tags":["go","frpc","xtcp","visitor","nat-hole"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}