{"record":{"id":"64e28cc8c9f34dbc","repo":"fatedier/frp","slug":"message-too-large-d-d","errorCode":null,"errorMessage":"message too large: %d > %d","messagePattern":"message too large: (.+?) > (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/vnet/message.go","lineNumber":44,"sourceCode":")\n\n// Format: [length(4 bytes)][data(length bytes)]\n\n// ReadMessage reads a framed message from the reader\nfunc ReadMessage(r io.Reader) ([]byte, error) {\n\t// Read length (4 bytes)\n\tvar length uint32\n\terr := binary.Read(r, binary.LittleEndian, &length)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read message length error: %w\", err)\n\t}\n\n\t// Check length to prevent DoS\n\tif length == 0 {\n\t\treturn nil, fmt.Errorf(\"message length is 0\")\n\t}\n\tif length > maxMessageSize {\n\t\treturn nil, fmt.Errorf(\"message too large: %d > %d\", length, maxMessageSize)\n\t}\n\n\t// Read message data\n\tdata := make([]byte, length)\n\t_, err = io.ReadFull(r, data)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read message data error: %w\", err)\n\t}\n\n\treturn data, nil\n}\n\n// WriteMessage writes a framed message to the writer\nfunc WriteMessage(w io.Writer, data []byte) error {\n\t// Get data length\n\tlength := uint32(len(data))\n\tif length == 0 {\n\t\treturn fmt.Errorf(\"message data length is 0\")","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/vnet/message.go#L26-L62","documentation":"DoS guard in vnet's ReadMessage: the declared frame length exceeds maxMessageSize (1 MiB). Any single vnet message must be at most 1MB, so a header claiming more is rejected before allocation, both to bound memory and because a huge length almost always indicates desync or a hostile/garbage stream rather than a real message.","triggerScenarios":"Length prefix reads a value > 1048576 due to stream desync (extra/missing bytes shifted the header position); random bytes hitting the port; a legitimate peer trying to send a vnet payload larger than 1MB, which the protocol forbids.","commonSituations":"Port scanners or health checks sending junk to a vnet-related listener; version mismatch changing framing; downstream code attempting to tunnel a huge datagram through vnet in one message.","solutions":["Reject/close the connection on this error — it is not retryable on the same stream","Ensure frpc and frps versions match exactly; framing changed across releases","Keep individual vnet messages under 1MB; chunk large payloads at the application layer","Keep the vnet port private (bind to trusted interface / firewall it) so random traffic cannot hit the framing reader"],"exampleFix":"// before: log and continue reading the same (desynced) stream\nif err != nil { log.Println(err); continue }\n\n// after: oversized length invalidates the stream\ndata, err := vnet.ReadMessage(r)\nif err != nil {\n    return fmt.Errorf(\"closing corrupt vnet stream: %w\", err)\n}","handlingStrategy":"validation","validationCode":"// sender side: keep each framed message under the protocol max\nconst maxMessageSize = 1024 * 1024\nfunc send(w io.Writer, payload []byte) error {\n    if len(payload) > maxMessageSize {\n        payload = chunk(payload) // application-level split\n    }\n    return vnet.WriteMessage(w, payload)\n}","typeGuard":null,"tryCatchPattern":"_, err := vnet.ReadMessage(r)\nif err != nil && strings.Contains(err.Error(), \"message too large\") {\n    conn.Close() // header untrustworthy: stream is desynced or hostile\n    return\n}","preventionTips":["Chunk large payloads below 1MB per message","Treat oversized-length as corruption: close the connection","Port-scan exposure turns framing guards into log noise — restrict access"],"tags":["vnet","protocol","security","memory"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}