fatedier/frp · error
received JSON UDP packet after binary codec negotiation
Error message
received JSON UDP packet after binary codec negotiation
What it means
V2BinaryUDPPacketReadWriter.ReadMsg received a JSON-encoded UDP packet (message type 13, V2TypeUDPPacket) after both sides had already negotiated the binary UDP packet codec. With the binary codec active, all UDP packets must arrive as type 19 (V2TypeUDPPacketBinary); a type-13 packet means the peer ignored or missed the codec negotiation. The connection is left in an inconsistent state, so the error should be treated as fatal.
Source
Thrown at pkg/msg/udp_binary.go:238
type V2BinaryUDPPacketReadWriter struct {
conn *wire.Conn
}
func NewV2BinaryUDPPacketReadWriter(rw io.ReadWriter) *V2BinaryUDPPacketReadWriter {
return &V2BinaryUDPPacketReadWriter{conn: wire.NewConn(rw)}
}
func (rw *V2BinaryUDPPacketReadWriter) ReadMsg() (Message, error) {
frame, err := rw.conn.ReadFrame()
if err != nil {
return nil, err
}
if isV2MessageType(frame, V2TypeUDPPacketBinary) {
return decodeV2BinaryUDPPacketFrame(frame)
}
if isV2MessageType(frame, V2TypeUDPPacket) {
return nil, fmt.Errorf("received JSON UDP packet after binary codec negotiation")
}
return DecodeV2MessageFrame(frame)
}
func (rw *V2BinaryUDPPacketReadWriter) ReadMsgInto(out Message) error {
frame, err := rw.conn.ReadFrame()
if err != nil {
return err
}
if packetOut, ok := out.(*UDPPacket); ok {
if !isV2MessageType(frame, V2TypeUDPPacketBinary) {
return unexpectedV2UDPPacketType(frame)
}
packet, err := decodeV2BinaryUDPPacketFrame(frame)
if err != nil {
return err
}
*packetOut = *packetView on GitHub (pinned to 6c8a8d0a97)
Solutions
- Align versions: run the same frp release on both frpc and frps.
- Check that udpPacketCodec = "binary" (or equivalent) is consistently configured on both ends of the tunnel, including visitor configs.
- If mixed versions are unavoidable, unset the binary UDP packet codec so both sides use the JSON codec.
- Restart the work connection after fixing config; the codec is chosen at connection setup.
Example fix
# before # frpc.toml sets binary UDP codec, frps is an older release # after # upgrade frps to the matching release, or on frpc.toml remove: # udpPacketCodec = "binary"
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure both ends agree before establishing the work connection
if udpPacketCodec != "" && wireProtocol != wire.ProtocolV2 {
return errors.New("binary UDP codec requires v2 wire protocol on both ends")
} Try / catch
if _, err := rw.ReadMsg(); err != nil {
if strings.Contains(err.Error(), "after binary codec negotiation") {
// peer did not adopt the binary codec: fix version/config on the peer, then reconnect
conn.Close()
}
} Prevention
- Pin frpc and frps to the same version in deployment manifests.
- Set udpPacketCodec identically in server, client, and visitor configs.
- Prefer disabling the binary codec over running mixed versions.
When it happens
Trigger: ReadMsg() on a work connection where frps and frpc disagreed on udpPacketCodec — e.g. the client sent binary packets while the server (older version, or config not applying) still emits JSON UDPPacket frames.
Common situations: frps upgraded but frpc not (binary codec exists only in newer versions); udpPacketCodec setting not propagated to the visitor side of an frp tunnel; a proxy between the peers strips or rewrites the negotiation.
Related errors
- UDP packet codec %q requires wire protocol v2
- truncated zone
- unexpected frame type %d, want %d
- message frame payload too short
- unexpected message type %d, want %d
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/b1056f78d7ef6a5d.
Report an issue: GitHub.