fatedier/frp · error
unexpected frame type %d, want %d
Error message
unexpected frame type %d, want %d
What it means
decodeV2BinaryUDPPacketFrame received a wire frame whose type is not FrameTypeMessage while decoding a binary UDP packet. Under the v2 wire protocol each UDP packet must ride in a message frame; other frame types (e.g. stream/control frames) indicate the stream is being misinterpreted. This is an internal consistency check after the frame was already routed here by type ID.
Source
Thrown at pkg/msg/udp_binary.go:288
frame, err := EncodeV2MessageFrame(message)
if err != nil {
return err
}
return rw.conn.WriteFrame(frame)
}
body, err := EncodeUDPPacketBinary(packet)
if err != nil {
return err
}
payload := make([]byte, 2+len(body))
binary.BigEndian.PutUint16(payload[:2], V2TypeUDPPacketBinary)
copy(payload[2:], body)
return rw.conn.WriteFrame(&wire.Frame{Type: wire.FrameTypeMessage, Payload: payload})
}
func decodeV2BinaryUDPPacketFrame(frame *wire.Frame) (*UDPPacket, error) {
if frame.Type != wire.FrameTypeMessage {
return nil, fmt.Errorf("unexpected frame type %d, want %d", frame.Type, wire.FrameTypeMessage)
}
if len(frame.Payload) < 2 {
return nil, fmt.Errorf("message frame payload too short")
}
if binary.BigEndian.Uint16(frame.Payload[:2]) != V2TypeUDPPacketBinary {
return nil, unexpectedV2UDPPacketType(frame)
}
return DecodeUDPPacketBinary(frame.Payload[2:])
}
func isV2MessageType(frame *wire.Frame, typeID uint16) bool {
return frame.Type == wire.FrameTypeMessage && len(frame.Payload) >= 2 && binary.BigEndian.Uint16(frame.Payload[:2]) == typeID
}
func unexpectedV2UDPPacketType(frame *wire.Frame) error {
if frame.Type != wire.FrameTypeMessage {
return fmt.Errorf("unexpected frame type %d, want %d", frame.Type, wire.FrameTypeMessage)
}View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Verify the io.ReadWriter passed to NewV2BinaryUDPPacketReadWriter is a connection that actually carries v2 wire frames (use wire.NewConn framing).
- Use NewUDPPacketReadWriter to construct the read writer — it selects the correct codec from the negotiated protocol strings.
- Treat the error as fatal for the connection and reconnect.
Example fix
// before rw := msg.NewV2BinaryUDPPacketReadWriter(rawConn) // rawConn lacks v2 wire framing // after rw, err := msg.NewUDPPacketReadWriter(rawConn, wire.ProtocolV2, wire.UDPPacketCodecBinary)
Defensive patterns
Strategy: validation
Validate before calling
// construct via the factory, never by hand
rw, err := msg.NewUDPPacketReadWriter(conn, wireProtocol, udpPacketCodec)
if err != nil {
return err
} Try / catch
if _, err := rw.ReadMsg(); err != nil {
if strings.Contains(err.Error(), "unexpected frame type") {
conn.Close() // wrong framing layer; reconnect
}
} Prevention
- Only pass v2-wire-framed connections to V2 message components.
- Use NewUDPPacketReadWriter so protocol/codec selection is centralized.
When it happens
Trigger: ReadMsg()/decodeV2BinaryUDPPacketFrame processes a frame with frame.Type != FrameTypeMessage — typically after stream desynchronization, or when a raw connection is fed into V2BinaryUDPPacketReadWriter without the v2 wire framing.
Common situations: Wrapping the wrong io.ReadWriter (a plain TCP conn expected to speak v2 wire frames); debugging harnesses replaying captured bytes; stream corruption from an intermediary.
Related errors
- message frame payload too short
- truncated zone
- received JSON UDP packet after binary codec negotiation
- unexpected message type %d, want %d
- UDP packet codec %q requires wire protocol v2
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/689f3da4d91c679f.
Report an issue: GitHub.