fatedier/frp · critical
listen on kcp udp address %s error: %v
Error message
listen on kcp udp address %s error: %v
What it means
When KCPBindPort > 0, frps opens a UDP listener for the KCP transport via netpkg.ListenKcp. Failure aborts startup with the offending address included. Like the TCP bind, this is fatal: the server process will not come up.
Source
Thrown at server/service.go:260
return nil, fmt.Errorf("create server listener error, %v", err)
}
svr.muxer = mux.NewMux(ln)
svr.muxer.SetKeepAlive(time.Duration(cfg.Transport.TCPKeepAlive) * time.Second)
go func() {
_ = svr.muxer.Serve()
}()
ln = svr.muxer.DefaultListener()
svr.listener = ln
log.Infof("frps tcp listen on %s", address)
// Listen for accepting connections from client using kcp protocol.
if cfg.KCPBindPort > 0 {
address := net.JoinHostPort(cfg.BindAddr, strconv.Itoa(cfg.KCPBindPort))
svr.kcpListener, err = netpkg.ListenKcp(address)
if err != nil {
return nil, fmt.Errorf("listen on kcp udp address %s error: %v", address, err)
}
log.Infof("frps kcp listen on udp %s", address)
}
if cfg.QUICBindPort > 0 {
address := net.JoinHostPort(cfg.BindAddr, strconv.Itoa(cfg.QUICBindPort))
quicTLSCfg := tlsConfig.Clone()
quicTLSCfg.NextProtos = []string{"frp"}
svr.quicListener, err = quic.ListenAddr(address, quicTLSCfg, &quic.Config{
MaxIdleTimeout: time.Duration(cfg.Transport.QUIC.MaxIdleTimeout) * time.Second,
MaxIncomingStreams: int64(cfg.Transport.QUIC.MaxIncomingStreams),
KeepAlivePeriod: time.Duration(cfg.Transport.QUIC.KeepalivePeriod) * time.Second,
})
if err != nil {
return nil, fmt.Errorf("listen on quic udp address %s error: %v", address, err)
}
log.Infof("frps quic listen on %s", address)
}View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Check ss -uln for the port and stop the conflicting process
- Pick a different kcpBindPort and mirror it in every frpc using protocol = kcp
- If UDP is blocked in the environment, remove kcpBindPort and use tcp/quan
- Verify firewall rules allow inbound UDP on that port
Example fix
# before kcpBindPort = 7000 # UDP already bound # after kcpBindPort = 7100
Defensive patterns
Strategy: validation
Validate before calling
if c, err := net.ListenPacket("udp", fmt.Sprintf("%s:%d", cfg.BindAddr, cfg.KCPBindPort)); err != nil {
return fmt.Errorf("kcp udp port %d unavailable: %w", cfg.KCPBindPort, err)
} else { c.Close() } Prevention
- Verify UDP sockets are allowed in the host/container before enabling KCP
- Prefer unique high ports for kcpBindPort to avoid common UDP collisions
- Keep an automatic fallback to tcp transport in client configs when kcp is unavailable
When it happens
Trigger: Another UDP service on the same KCPBindPort; firewall/SELinux blocking UDP bind; BindAddr misconfigured; a previous frps still holding the socket.
Common situations: kcpBindPort collides with another UDP service (Quake-style servers, DTLS, DNS on odd ports); cloud environments filtering UDP; duplicated frps instances.
Related errors
- listen on quic udp address %s error: %v
- create server listener error, %v
- create vhost http listener error, %v
- dial udp error: %v
- create kcp connection from udp connection error: %v
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/82c33ed8d7b8952e.
Report an issue: GitHub.