{"record":{"id":"0ae728c416e23a6e","repo":"fatedier/frp","slug":"frame-payload-length-d-exceeds-limit-d","errorCode":null,"errorMessage":"frame payload length %d exceeds limit %d","messagePattern":"frame payload length (.+?) exceeds limit (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/proto/wire/wire.go","lineNumber":76,"sourceCode":"\t\trw:                  rw,\n\t\tmaxFramePayloadSize: DefaultMaxFramePayloadSize,\n\t}\n}\n\nfunc (c *Conn) ReadFrame() (*Frame, error) {\n\theader := make([]byte, 8)\n\tif _, err := io.ReadFull(c.rw, header); err != nil {\n\t\treturn nil, err\n\t}\n\n\tframeType := binary.BigEndian.Uint16(header[0:2])\n\tflags := binary.BigEndian.Uint16(header[2:4])\n\tlength := binary.BigEndian.Uint32(header[4:8])\n\tif flags != 0 {\n\t\treturn nil, fmt.Errorf(\"unsupported frame flags: %d\", flags)\n\t}\n\tif length > c.maxFramePayloadSize {\n\t\treturn nil, fmt.Errorf(\"frame payload length %d exceeds limit %d\", length, c.maxFramePayloadSize)\n\t}\n\n\tpayload := make([]byte, length)\n\tif _, err := io.ReadFull(c.rw, payload); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Frame{\n\t\tType:    frameType,\n\t\tFlags:   flags,\n\t\tPayload: payload,\n\t}, nil\n}\n\nfunc (c *Conn) WriteFrame(f *Frame) error {\n\tif f.Flags != 0 {\n\t\treturn fmt.Errorf(\"unsupported frame flags: %d\", f.Flags)\n\t}\n\tif len(f.Payload) > int(c.maxFramePayloadSize) {","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/proto/wire/wire.go#L58-L94","documentation":"Returned by Conn.ReadFrame when the header's declared payload length exceeds the connection's maxFramePayloadSize (DefaultMaxFramePayloadSize = 64 KiB). This is a DoS guard: it refuses to allocate a buffer based on an attacker-controlled length field before reading the payload.","triggerScenarios":"A peer declares length > 65536 in the 4-byte big-endian length field, either legitimately (trying to send a huge JSON message) or because the stream is desynchronized and the length bytes are actually payload data.","commonSituations":"A proxy status or config message whose JSON exceeds 64 KiB; framing bugs where a frame is written with the wrong length; malicious clients probing the server.","solutions":["Keep framed JSON messages under 64 KiB — split large payloads (e.g. paginate status responses) instead of raising the cap.","If both ends are yours and you truly need bigger frames, construct the Conn with a larger maxFramePayloadSize on both sides (NewConn sets the default; keep them symmetric).","If occasional, suspect stream desynchronization — audit all writers to the connection for interleaved writes."],"exampleFix":"// before\nframe, _ := wire.NewJSONFrame(wire.FrameTypeX, hugeStatusObject) // payload > 64KiB\nconn.WriteFrame(frame)\n\n// after\n// chunk or shrink the payload, e.g. per-proxy status requests\nfor name, statuses := range allStatus { sendFrame(conn, name, statuses) }","handlingStrategy":"validation","validationCode":"// sender side, before writing:\npayload, err := json.Marshal(msg)\nif err != nil { return err }\nif len(payload) > int(wire.DefaultMaxFramePayloadSize) {\n    return fmt.Errorf(\"message %d bytes exceeds frame limit %d\", len(payload), wire.DefaultMaxFramePayloadSize)\n}","typeGuard":null,"tryCatchPattern":"if _, err := conn.ReadFrame(); err != nil {\n    if strings.Contains(err.Error(), \"exceeds limit\") {\n        // peer sent/claimed oversized frame: close conn (possible desync or DoS)\n        conn.Close()\n    }\n    return err\n}","preventionTips":["Design messages to stay well under 64 KiB; paginate bulky data.","If raising the limit, raise it symmetrically on both endpoints."],"tags":["protocol","framing","limits","dos-guard","go"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}