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

  1. Check ss -uln for the port and stop the conflicting process
  2. Pick a different kcpBindPort and mirror it in every frpc using protocol = kcp
  3. If UDP is blocked in the environment, remove kcpBindPort and use tcp/quan
  4. 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

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


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