fatedier/frp · error

UDP packet codec %q requires wire protocol v2

Error message

UDP packet codec %q requires wire protocol v2

What it means

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.

Source

Thrown at pkg/msg/udp_binary.go:323

		return fmt.Errorf("unexpected frame type %d, want %d", frame.Type, wire.FrameTypeMessage)
	}
	if len(frame.Payload) < 2 {
		return fmt.Errorf("message frame payload too short")
	}
	typeID := binary.BigEndian.Uint16(frame.Payload[:2])
	if typeID == V2TypeUDPPacket {
		return fmt.Errorf("received JSON UDP packet after binary codec negotiation")
	}
	return fmt.Errorf("unexpected message type %d, want %d", typeID, V2TypeUDPPacketBinary)
}

// NewUDPPacketReadWriter selects the negotiated packet codec without changing
// the framing or codecs used by non-UDP messages on the work connection.
func NewUDPPacketReadWriter(rw io.ReadWriter, wireProtocol, udpPacketCodec string) (ReadWriter, error) {
	switch wireProtocol {
	case "", wire.ProtocolV1:
		if udpPacketCodec != "" {
			return nil, fmt.Errorf("UDP packet codec %q requires wire protocol v2", udpPacketCodec)
		}
		return NewV1ReadWriter(rw), nil
	case wire.ProtocolV2:
		switch udpPacketCodec {
		case "":
			return NewV2ReadWriter(rw), nil
		case wire.UDPPacketCodecBinary:
			return NewV2BinaryUDPPacketReadWriter(rw), nil
		default:
			return nil, fmt.Errorf("unsupported UDP packet codec %q", udpPacketCodec)
		}
	default:
		return nil, fmt.Errorf("unsupported wire protocol %q", wireProtocol)
	}
}

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Set the wire protocol to v2 wherever you set the UDP packet codec: NewUDPPacketReadWriter(rw, wire.ProtocolV2, wire.UDPPacketCodecBinary).
  2. In TOML config, ensure the transport/protocol selection enabling v2 matches on both frpc and frps.
  3. Or drop udpPacketCodec to keep the v1 JSON codec.

Example fix

// before
rw, err := msg.NewUDPPacketReadWriter(conn, wire.ProtocolV1, wire.UDPPacketCodecBinary) // error

// after
rw, err := msg.NewUDPPacketReadWriter(conn, wire.ProtocolV2, wire.UDPPacketCodecBinary)
Defensive patterns

Strategy: validation

Validate before calling

if udpPacketCodec != "" && wireProtocol != wire.ProtocolV2 {
	return fmt.Errorf("UDP packet codec %q requires wire protocol v2", udpPacketCodec)
}

Prevention

When it happens

Trigger: Calling NewUDPPacketReadWriter(rw, "v1", "binary") or NewUDPPacketReadWriter(rw, "", wire.UDPPacketCodecBinary) — i.e. config leaves wireProtocol at default v1 while setting udpPacketCodec to binary.

Common situations: Enabling the binary UDP codec in frpc.toml without also setting the v2 wire protocol; default config templates that omit wireProtocol; upgrading configs piecemeal.

Related errors


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/016c05560bc43f3c. Report an issue: GitHub.