fatedier/frp · error

unknown v2 message type %d

Error message

unknown v2 message type %d

What it means

DecodeV2MessageFrame looked up the 2-byte type ID in v2MsgReflectTypeMap (built from the 18 registered message types, IDs 1-18 plus 19 for binary UDP) and found no entry. The peer sent a type ID this build does not know — either a message kind introduced in a newer frp version, a removed legacy ID, or garbage bytes. Because the JSON body layout is type-specific, decoding cannot proceed.

Source

Thrown at pkg/msg/wire_v2.go:133

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 {
		return fmt.Errorf("unexpected frame type %d, want %d", f.Type, wire.FrameTypeMessage)
	}
	if len(f.Payload) < 2 {
		return fmt.Errorf("message frame payload too short")
	}

	typeID := binary.BigEndian.Uint16(f.Payload[:2])
	outType := reflect.TypeOf(out)

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Upgrade both frpc and frps to the same frp release.
  2. If you fork the protocol, allocate new type IDs consistently on both sides and rebuild both binaries.
  3. Treat the error as fatal for the connection — the following JSON body cannot be interpreted.
  4. Verify you are connecting to an actual frp v2 endpoint, not some other service on that port.
Defensive patterns

Strategy: try-catch

Try / catch

m, err := msg.DecodeV2MessageFrame(f)
if err != nil {
	if strings.Contains(err.Error(), "unknown v2 message type") {
		// peer speaks a newer/older protocol: version skew — upgrade both sides, then reconnect
		conn.Close()
		return err
	}
}

Prevention

When it happens

Trigger: A newer frps sends a message type not present in the older frpc's registry (forward-compat gap); a peer from a different protocol entirely; fuzzed type IDs; desynchronized stream landing on this decoder.

Common situations: Version skew between frpc and frps after upgrading only one side; custom forks that add message types without coordinating IDs; connecting a non-frp service to an frp port.

Related errors


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