fatedier/frp · error
unexpected frame type %d, want %d
Error message
unexpected frame type %d, want %d
What it means
DecodeV2MessageFrame (and DecodeV2MessageFrameInto) requires the frame to be a FrameTypeMessage frame; v2 messages are JSON bodies prefixed with a 2-byte type ID and carried exclusively in message frames. A frame of any other wire type (stream/keepalive/etc.) reaching this decoder means the caller is using the v2 message decoder on a non-message frame — a framing-layer misuse or stream corruption.
Source
Thrown at pkg/msg/wire_v2.go:125
func (rw *V2ReadWriter) ReadMsgInto(m Message) error {
f, err := rw.conn.ReadFrame()
if err != nil {
return err
}
return DecodeV2MessageFrameInto(f, m)
}
func (rw *V2ReadWriter) WriteMsg(m Message) error {
f, err := EncodeV2MessageFrame(m)
if err != nil {
return err
}
return rw.conn.WriteFrame(f)
}
func DecodeV2MessageFrame(f *wire.Frame) (Message, error) {
if f.Type != wire.FrameTypeMessage {
return nil, fmt.Errorf("unexpected frame type %d, want %d", f.Type, wire.FrameTypeMessage)
}
if len(f.Payload) < 2 {
return nil, fmt.Errorf("message frame payload too short")
}
typeID := binary.BigEndian.Uint16(f.Payload[:2])
t, ok := v2MsgReflectTypeMap[typeID]
if !ok {
return nil, fmt.Errorf("unknown v2 message type %d", typeID)
}
m := reflect.New(t).Interface()
if err := json.Unmarshal(f.Payload[2:], m); err != nil {
return nil, err
}
return m, nil
}
func DecodeV2MessageFrameInto(f *wire.Frame, out Message) error {
if f.Type != wire.FrameTypeMessage {View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Filter by frame.Type == wire.FrameTypeMessage before calling DecodeV2MessageFrame.
- Prefer the ReadWriter's ReadMsg, which only ever handles the appropriate frames per negotiated protocol.
- Treat unexpected frame types at runtime as fatal for the connection.
Example fix
// before
m, err := msg.DecodeV2MessageFrame(frame) // frame may be a stream frame
// after
if frame.Type != wire.FrameTypeMessage {
return fmt.Errorf("not a message frame: %d", frame.Type)
}
m, err := msg.DecodeV2MessageFrame(frame) Defensive patterns
Strategy: validation
Validate before calling
if frame.Type != wire.FrameTypeMessage {
return fmt.Errorf("skipping non-message frame type %d", frame.Type)
} Try / catch
m, err := msg.DecodeV2MessageFrame(f)
if err != nil {
if strings.HasPrefix(err.Error(), "unexpected frame type") {
// framing misuse or corruption: drop the connection
conn.Close()
return err
}
} Prevention
- Filter frames by type before decoding in custom pipelines.
- Prefer ReadMsg over manual frame decoding.
When it happens
Trigger: Passing a non-message wire.Frame to DecodeV2MessageFrame directly, or ReadMsg() on a V2ReadWriter whose underlying wire.Conn delivered a non-message frame — usually a mis-wired connection or fuzzed input.
Common situations: Custom code decodes raw frames manually and hands the wrong frame type; stream desync after an earlier error; replay harnesses feeding arbitrary frames.
Related errors
- message frame payload too short
- message frame payload too short
- unexpected frame type %d, want %d
- unsupported wire protocol %q
- invalid pool count %d, must be non-negative
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/ac1b61246df6651a.
Report an issue: GitHub.