ginuerzh/gost · error

kcp: wrong connection type

Error message

kcp: wrong connection type

What it means

kcpTransporter.initSession requires the conn it received to implement net.PacketConn, because the KCP protocol is built on top of UDP-style packet sockets. If the handed-over connection is a stream conn (e.g. *net.TCPConn or a wrapped/tunneled conn), it returns this error before creating the KCP session.

Source

Thrown at kcp.go:219

			return nil, err
		}
		session = s
		tr.sessions[opts.Addr] = session
	}
	cc, err := session.GetConn()
	if err != nil {
		session.Close()
		delete(tr.sessions, opts.Addr)
		return nil, err
	}

	return cc, nil
}

func (tr *kcpTransporter) initSession(addr string, conn net.Conn, config *KCPConfig) (*muxSession, error) {
	pc, ok := conn.(net.PacketConn)
	if !ok {
		return nil, errors.New("kcp: wrong connection type")
	}

	kcpconn, err := kcp.NewConn(addr,
		blockCrypt(config.Key, config.Crypt, KCPSalt),
		config.DataShard, config.ParityShard, pc)
	if err != nil {
		return nil, err
	}

	kcpconn.SetStreamMode(true)
	kcpconn.SetWriteDelay(false)
	kcpconn.SetNoDelay(config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
	kcpconn.SetWindowSize(config.SndWnd, config.RcvWnd)
	kcpconn.SetMtu(config.MTU)
	kcpconn.SetACKNoDelay(config.AckNodelay)

	if config.DSCP > 0 {
		if err := kcpconn.SetDSCP(config.DSCP); err != nil {

View on GitHub (pinned to a33fdbf4c9)

Solutions

  1. Ensure the kcp transport dials via its own UDP dialer so a net.PacketConn is passed to initSession.
  2. Do not wrap the conn in intermediaries before kcp.Handshake; if wrapping is required, implement/unwrap to expose net.PacketConn.
  3. Verify node/chain config so kcp:// URLs use the kcp dialer, not the tcp one.
  4. If you must run KCP over a stream, use a KCP variant that supports stream conns instead of this transporter.

Example fix

// before
conn, _ := net.Dial("tcp", addr) // stream conn
session, err := kcpTransporter.Handshake(conn, addr)
// after
conn, err := net.Dial("udp", addr) // PacketConn
session, err := kcpTransporter.Handshake(conn, addr)
Defensive patterns

Strategy: validation

Validate before calling

if _, ok := conn.(net.PacketConn); !ok {
	return errors.New("kcp handshake requires a net.PacketConn (UDP) connection")
}

Type guard

func isPacketConn(c net.Conn) (net.PacketConn, bool) {
	pc, ok := c.(net.PacketConn)
	return pc, ok
}

Try / catch

session, err := tr.Handshake(conn, addr)
if err != nil && strings.Contains(err.Error(), "wrong connection type") {
	// dial UDP and retry
	pc, derr := net.Dial("udp", addr)
	if derr != nil { return derr }
	session, err = tr.Handshake(pc, addr)
}

Prevention

When it happens

Trigger: Calling Handshake on the kcp transporter with a conn that is not a net.PacketConn — e.g. configuring the kcp transport but the dialer produced a stream connection, or passing a wrapped/muxed conn into initSession via Handshake.

Common situations: Misconfigured transport chain (kcp selected but underlying dialer is TCP); proxy chains wrapping conns so the concrete PacketConn type is lost; passing a net.Conn from another transport into the kcp handshake path.

Related errors


AI-assisted analysis of ginuerzh/gost@a33fdbf4c9 (2026-09-02). Data as JSON: /api/errors/cd402697f9168a45. Report an issue: GitHub.