XTLS/Xray-core · warning

discarding fragmented payload.

Error message

discarding fragmented payload.

What it means

Thrown when decoding a SOCKS5 UDP packet whose fragment byte (byte 2 of the header) is non-zero, meaning the client used SOCKS5 UDP fragmentation (RFC 1928 FRAG). Xray does not reassemble fragmented UDP datagrams, so it discards them instead of delivering corrupt payloads.

Source

Thrown at proxy/socks/protocol.go:355

	common.Must(buffer.WriteByte(errCode))
	portBytes := buffer.Extend(2)
	binary.BigEndian.PutUint16(portBytes, port.Value())
	common.Must2(buffer.Write(address.IP()))
	return buf.WriteAllBytes(writer, buffer.Bytes(), nil)
}

func DecodeUDPPacket(packet *buf.Buffer) (*protocol.RequestHeader, error) {
	if packet.Len() < 5 {
		return nil, errors.New("insufficient length of packet.")
	}
	request := &protocol.RequestHeader{
		Version: socks5Version,
		Command: protocol.RequestCommandUDP,
	}

	// packet[0] and packet[1] are reserved
	if packet.Byte(2) != 0 /* fragments */ {
		return nil, errors.New("discarding fragmented payload.")
	}

	packet.Advance(3)

	addr, port, err := addrParser.ReadAddressPort(nil, packet)
	if err != nil {
		return nil, errors.New("failed to read UDP header").Base(err)
	}
	request.Address = addr
	request.Port = port
	return request, nil
}

func EncodeUDPPacket(request *protocol.RequestHeader, data []byte) (*buf.Buffer, error) {
	b := buf.New()
	common.Must2(b.Write([]byte{0, 0, 0 /* Fragment */}))
	if err := addrParser.WriteAddressPort(b, request.Address, request.Port); err != nil {
		b.Release()

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Configure the client SOCKS5 library to disable SOCKS-level fragmentation (always send FRAG=0 and rely on IP fragmentation or smaller payloads).
  2. Lower the client application's UDP datagram size below the path MTU so fragmentation is never attempted.
  3. If you cannot change the client, switch the tunnel to a transport that carries UDP natively without SOCKS framing.

Example fix

// client side, e.g. go-socks5 style writer
// before
header[2] = fragNumber // FRAG != 0

// after
header[2] = 0 // never SOCKS-fragment; keep datagrams whole
_ = fragNumber
Defensive patterns

Strategy: validation

Validate before calling

// before decoding, check the fragment byte
if packet.Len() >= 3 && packet.Byte(2) != 0 {
	// client used SOCKS5 fragmentation; unsupported
	packet.Release()
	continue
}

Try / catch

if _, err := socks.DecodeUDPPacket(packet); err != nil && strings.Contains(err.Error(), "fragmented") {
	metrics.FragmentedDrops++
	continue
}

Prevention

When it happens

Trigger: A SOCKS5 client sends UDP ASSOCIATE traffic with FRAG != 0 — i.e. it split a large datagram into fragments per RFC 1928. Any client that sets the fragment field (some tun2socks stacks, rare BitTorrent/DHT implementations) triggers this on every fragmented datagram.

Common situations: Large UDP payloads exceeding the client's path MTU where the client's SOCKS library fragments rather than relying on IP fragmentation; legacy or spec-literal SOCKS5 client libraries; games or VoIP with oversized datagrams.

Related errors


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