{"record":{"id":"016c05560bc43f3c","repo":"fatedier/frp","slug":"udp-packet-codec-q-requires-wire-protocol-v2","errorCode":null,"errorMessage":"UDP packet codec %q requires wire protocol v2","messagePattern":"UDP packet codec %q requires wire protocol v2","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/msg/udp_binary.go","lineNumber":323,"sourceCode":"\t\treturn fmt.Errorf(\"unexpected frame type %d, want %d\", frame.Type, wire.FrameTypeMessage)\n\t}\n\tif len(frame.Payload) < 2 {\n\t\treturn fmt.Errorf(\"message frame payload too short\")\n\t}\n\ttypeID := binary.BigEndian.Uint16(frame.Payload[:2])\n\tif typeID == V2TypeUDPPacket {\n\t\treturn fmt.Errorf(\"received JSON UDP packet after binary codec negotiation\")\n\t}\n\treturn fmt.Errorf(\"unexpected message type %d, want %d\", typeID, V2TypeUDPPacketBinary)\n}\n\n// NewUDPPacketReadWriter selects the negotiated packet codec without changing\n// the framing or codecs used by non-UDP messages on the work connection.\nfunc NewUDPPacketReadWriter(rw io.ReadWriter, wireProtocol, udpPacketCodec string) (ReadWriter, error) {\n\tswitch wireProtocol {\n\tcase \"\", wire.ProtocolV1:\n\t\tif udpPacketCodec != \"\" {\n\t\t\treturn nil, fmt.Errorf(\"UDP packet codec %q requires wire protocol v2\", udpPacketCodec)\n\t\t}\n\t\treturn NewV1ReadWriter(rw), nil\n\tcase wire.ProtocolV2:\n\t\tswitch udpPacketCodec {\n\t\tcase \"\":\n\t\t\treturn NewV2ReadWriter(rw), nil\n\t\tcase wire.UDPPacketCodecBinary:\n\t\t\treturn NewV2BinaryUDPPacketReadWriter(rw), nil\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unsupported UDP packet codec %q\", udpPacketCodec)\n\t\t}\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unsupported wire protocol %q\", wireProtocol)\n\t}\n}\n","sourceCodeStart":305,"sourceCodeEnd":339,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/msg/udp_binary.go#L305-L339","documentation":"NewUDPPacketReadWriter refuses a non-empty udpPacketCodec when the wire protocol is v1 (\"\" or \"v1\"). The binary UDP packet codec is implemented on top of v2 message framing (2-byte type IDs), which v1 framing cannot carry, so the combination is rejected up front instead of failing per-packet later.","triggerScenarios":"Calling NewUDPPacketReadWriter(rw, \"v1\", \"binary\") or NewUDPPacketReadWriter(rw, \"\", wire.UDPPacketCodecBinary) — i.e. config leaves wireProtocol at default v1 while setting udpPacketCodec to binary.","commonSituations":"Enabling the binary UDP codec in frpc.toml without also setting the v2 wire protocol; default config templates that omit wireProtocol; upgrading configs piecemeal.","solutions":["Set the wire protocol to v2 wherever you set the UDP packet codec: NewUDPPacketReadWriter(rw, wire.ProtocolV2, wire.UDPPacketCodecBinary).","In TOML config, ensure the transport/protocol selection enabling v2 matches on both frpc and frps.","Or drop udpPacketCodec to keep the v1 JSON codec."],"exampleFix":"// before\nrw, err := msg.NewUDPPacketReadWriter(conn, wire.ProtocolV1, wire.UDPPacketCodecBinary) // error\n\n// after\nrw, err := msg.NewUDPPacketReadWriter(conn, wire.ProtocolV2, wire.UDPPacketCodecBinary)","handlingStrategy":"validation","validationCode":"if udpPacketCodec != \"\" && wireProtocol != wire.ProtocolV2 {\n\treturn fmt.Errorf(\"UDP packet codec %q requires wire protocol v2\", udpPacketCodec)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always set wireProtocol and udpPacketCodec together from one validated config struct.","Use the wire package constants instead of string literals."],"tags":["config","protocol","udp","frp","version-mismatch"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}