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

  1. Filter by frame.Type == wire.FrameTypeMessage before calling DecodeV2MessageFrame.
  2. Prefer the ReadWriter's ReadMsg, which only ever handles the appropriate frames per negotiated protocol.
  3. 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

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


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