fatedier/frp · error

message frame payload too short

Error message

message frame payload too short

What it means

decodeV2BinaryUDPPacketFrame requires at least 2 payload bytes (the big-endian message type ID) before it can dispatch. A frame with a 0- or 1-byte payload cannot carry a type ID, so it is rejected. Like other framing checks here, it indicates malformed input rather than a recoverable condition.

Source

Thrown at pkg/msg/udp_binary.go:291

		}
		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)
	}
	if len(frame.Payload) < 2 {
		return fmt.Errorf("message frame payload too short")
	}

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. When constructing frames in tests or custom code, always prepend the 2-byte type ID to the payload.
  2. Prefer EncodeV2MessageFrame / the library's WriteMsg instead of hand-building frames.
  3. Treat receipt at runtime as a fatal connection error.

Example fix

// before (test builds frame without type prefix)
frame := &wire.Frame{Type: wire.FrameTypeMessage, Payload: []byte{0x01}}

// after
payload := binary.BigEndian.AppendUint16(nil, uint16(msg.V2TypeUDPPacketBinary))
frame := &wire.Frame{Type: wire.FrameTypeMessage, Payload: payload}
Defensive patterns

Strategy: validation

Validate before calling

func validMessageFrame(f *wire.Frame) bool {
	return f.Type == wire.FrameTypeMessage && len(f.Payload) >= 2
}

Prevention

When it happens

Trigger: A FrameTypeMessage frame with payload shorter than 2 bytes reaches decodeV2BinaryUDPPacketFrame — crafted input, stream desynchronization, or a bug in a custom frame producer.

Common situations: Fuzzing the decoder with empty payloads; a truncated write on the peer side; unit-test fixtures built with an empty payload.

Related errors


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