{"record":{"id":"ab3c6191d4ed0754","repo":"XTLS/Xray-core","slug":"discarding-fragmented-payload","errorCode":null,"errorMessage":"discarding fragmented payload.","messagePattern":"discarding fragmented payload\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"proxy/socks/protocol.go","lineNumber":355,"sourceCode":"\tcommon.Must(buffer.WriteByte(errCode))\n\tportBytes := buffer.Extend(2)\n\tbinary.BigEndian.PutUint16(portBytes, port.Value())\n\tcommon.Must2(buffer.Write(address.IP()))\n\treturn buf.WriteAllBytes(writer, buffer.Bytes(), nil)\n}\n\nfunc DecodeUDPPacket(packet *buf.Buffer) (*protocol.RequestHeader, error) {\n\tif packet.Len() < 5 {\n\t\treturn nil, errors.New(\"insufficient length of packet.\")\n\t}\n\trequest := &protocol.RequestHeader{\n\t\tVersion: socks5Version,\n\t\tCommand: protocol.RequestCommandUDP,\n\t}\n\n\t// packet[0] and packet[1] are reserved\n\tif packet.Byte(2) != 0 /* fragments */ {\n\t\treturn nil, errors.New(\"discarding fragmented payload.\")\n\t}\n\n\tpacket.Advance(3)\n\n\taddr, port, err := addrParser.ReadAddressPort(nil, packet)\n\tif err != nil {\n\t\treturn nil, errors.New(\"failed to read UDP header\").Base(err)\n\t}\n\trequest.Address = addr\n\trequest.Port = port\n\treturn request, nil\n}\n\nfunc EncodeUDPPacket(request *protocol.RequestHeader, data []byte) (*buf.Buffer, error) {\n\tb := buf.New()\n\tcommon.Must2(b.Write([]byte{0, 0, 0 /* Fragment */}))\n\tif err := addrParser.WriteAddressPort(b, request.Address, request.Port); err != nil {\n\t\tb.Release()","sourceCodeStart":337,"sourceCodeEnd":373,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/proxy/socks/protocol.go#L337-L373","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Configure the client SOCKS5 library to disable SOCKS-level fragmentation (always send FRAG=0 and rely on IP fragmentation or smaller payloads).","Lower the client application's UDP datagram size below the path MTU so fragmentation is never attempted.","If you cannot change the client, switch the tunnel to a transport that carries UDP natively without SOCKS framing."],"exampleFix":"// client side, e.g. go-socks5 style writer\n// before\nheader[2] = fragNumber // FRAG != 0\n\n// after\nheader[2] = 0 // never SOCKS-fragment; keep datagrams whole\n_ = fragNumber","handlingStrategy":"validation","validationCode":"// before decoding, check the fragment byte\nif packet.Len() >= 3 && packet.Byte(2) != 0 {\n\t// client used SOCKS5 fragmentation; unsupported\n\tpacket.Release()\n\tcontinue\n}","typeGuard":null,"tryCatchPattern":"if _, err := socks.DecodeUDPPacket(packet); err != nil && strings.Contains(err.Error(), \"fragmented\") {\n\tmetrics.FragmentedDrops++\n\tcontinue\n}","preventionTips":["Configure client SOCKS5 stacks to always send FRAG=0.","Cap application UDP datagram size below path MTU.","Prefer transports with native UDP for fragmentation-prone traffic."],"tags":["socks","socks5","udp","fragmentation","rfc1928"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}