XTLS/Xray-core · error

write handshake packet: %w

Error message

write handshake packet: %w

What it means

Wraps a writePacket failure when sending the Minecraft handshake packet (packet ID 0x00 with protocol version 775, host, port 25565, next state 2). The %w chains the underlying write error on the client's writer, so the cause is almost always the TCP connection state.

Source

Thrown at transport/internet/finalmask/xmc/client.go:104

		serverPort      UnsignedShort = UnsignedShort(25565)
		nextState       Varint        = Varint(2)
	)

	host, portString, err := net.SplitHostPort(c.c.RemoteAddr().String())
	if err == nil {
		port, err := strconv.Atoi(portString)
		if err == nil {
			serverPort = UnsignedShort(port)
		}

		if serverAddress == "" {
			serverAddress = String(host)
		}
	}

	err = writePacket(c.writer, 0x00, &protocolVersion, &serverAddress, &serverPort, &nextState)
	if err != nil {
		return fmt.Errorf("write handshake packet: %w", err)
	}

	// Login Start
	randomProfile, err := rand.Int(rand.Reader, big.NewInt(int64(len(c.profiles))))
	if err != nil {
		return fmt.Errorf("select profile: %w", err)
	}
	selectedProfile := c.profiles[randomProfile.Int64()]
	username := String(selectedProfile.Username)

	err = writePacket(c.writer, 0x00, &username, &selectedProfile.UUID)
	if err != nil {
		return fmt.Errorf("write login start: %w", err)
	}

	// Encryption Request
	pkt, err := readPacket(c.reader)
	if err != nil {

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Verify the target hostname/port and that the server speaks the Minecraft protocol.
  2. Unwrap the %w to distinguish i/o timeout from broken pipe/ECONNRESET.
  3. Retry the connection; transient resets during dial are common, and the transport re-handshakes on a fresh conn.
Defensive patterns

Strategy: retry

Try / catch

if err := cc.Handshake(); err != nil {
	if isTransientWriteErr(err) { // ECONNRESET, broken pipe, i/o timeout
		return backoff.RetryDial(ctx, target) // fresh conn, full re-handshake
	}
	return err
}

Prevention

When it happens

Trigger: The socket breaking between TCP connect and the first write: server RST, idle-timeout disconnect, deadline expired, or a proxy in front dropping the connection. writePacket serializes Varint/String/UnsignedShort fields and fails on the first short write.

Common situations: Pointing the client at a host that immediately closes connections (wrong port, firewall REJECT with reset); handshake deadline too short for a slow link; NAT middlebox killing the flow.

Understand the failure class

Related errors


AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15). Data as JSON: /api/errors/8655d452e6639eaa. Report an issue: GitHub.