fatedier/frp · error

unsupported frame flags: %d

Error message

unsupported frame flags: %d

What it means

Returned by Conn.ReadFrame when the incoming 8-byte frame header has a non-zero flags field. The framing protocol (type uint16, flags uint16, length uint32) reserves flags for future use but this build requires them to be zero; any non-zero value means the stream is corrupt or the peer speaks a newer protocol.

Source

Thrown at pkg/proto/wire/wire.go:73

func NewConn(rw io.ReadWriter) *Conn {
	return &Conn{
		rw:                  rw,
		maxFramePayloadSize: DefaultMaxFramePayloadSize,
	}
}

func (c *Conn) ReadFrame() (*Frame, error) {
	header := make([]byte, 8)
	if _, err := io.ReadFull(c.rw, header); err != nil {
		return nil, err
	}

	frameType := binary.BigEndian.Uint16(header[0:2])
	flags := binary.BigEndian.Uint16(header[2:4])
	length := binary.BigEndian.Uint32(header[4:8])
	if flags != 0 {
		return nil, fmt.Errorf("unsupported frame flags: %d", flags)
	}
	if length > c.maxFramePayloadSize {
		return nil, fmt.Errorf("frame payload length %d exceeds limit %d", length, c.maxFramePayloadSize)
	}

	payload := make([]byte, length)
	if _, err := io.ReadFull(c.rw, payload); err != nil {
		return nil, err
	}
	return &Frame{
		Type:    frameType,
		Flags:   flags,
		Payload: payload,
	}, nil
}

func (c *Conn) WriteFrame(f *Frame) error {
	if f.Flags != 0 {

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Check for concurrent writers on the same connection — interleaved writes corrupt the frame stream.
  2. Verify both peers use the same protocol version; flag bits imply a protocol this build does not understand.
  3. If it happens after a previous error, close the connection: the stream state is unrecoverable.
Defensive patterns

Strategy: try-catch

Try / catch

if _, err := conn.ReadFrame(); err != nil {
    if strings.Contains(err.Error(), "unsupported frame flags") {
        // stream desynced or newer peer protocol: close conn permanently, no recovery
        conn.Close()
    }
    return err
}

Prevention

When it happens

Trigger: The peer (or a middlebox) writes a frame with flags != 0; or the stream has desynchronized so that header bytes 2-4 are actually payload data. ReadFrame reads the header with io.ReadFull and immediately rejects non-zero flags.

Common situations: Stream desynchronization after an earlier partial read or a wrong payload-length assumption; connecting a newer frp peer that started using flag bits; writing raw bytes to the socket out-of-band from another goroutine.

Related errors


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