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
- Check for concurrent writers on the same connection — interleaved writes corrupt the frame stream.
- Verify both peers use the same protocol version; flag bits imply a protocol this build does not understand.
- 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
- Serialize all writes to a Conn from a single goroutine or a mutex.
- Treat any framing error as fatal for the connection — never continue reading after one.
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
- frame payload length %d exceeds limit %d
- unexpected frame type %d, want %d
- failed to write temp file: %w
- failed to sync temp file: %w
- failed to close temp file: %w
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/eae6d0cb6c33a1ef.
Report an issue: GitHub.