{"record":{"id":"2a03995e5d98b046","repo":"fatedier/frp","slug":"message-length-is-0","errorCode":null,"errorMessage":"message length is 0","messagePattern":"message length is 0","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/vnet/message.go","lineNumber":41,"sourceCode":"// Maximum message size\nconst (\n\tmaxMessageSize = 1024 * 1024 // 1MB\n)\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","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/vnet/message.go#L23-L59","documentation":"Protocol validation error in vnet's ReadMessage: the 4-byte length prefix was read successfully but its value is 0. A zero-length frame is invalid by design (it would allocate nothing and make EOF/parse behaviour ambiguous), so it is rejected as a DoS/robustness guard. Receiving it means the stream is not speaking the expected [4-byte len][data] framing at that byte offset.","triggerScenarios":"A peer (or anything sharing the connection) sends a 4-zero-byte sequence; the reader's stream is desynchronized because a previous frame was partially consumed or extra bytes were injected; connecting a non-vnet client to a vnet port so arbitrary bytes are interpreted as a length header.","commonSituations":"Version skew between frpc and frps changing message framing; a load balancer or proxy injecting a PROXY/health-check line into the stream; leftover buffered bytes after an earlier framing error; probing/scanning the vnet port with tools like nc.","solutions":["Confirm frpc and frps run the same frp version (frpc -v / frps -v), upgrade both together","Make sure nothing else terminates/inspects the connection (L7 proxies, health checkers) — vnet needs a byte-clean stream","If it recurs with matching versions, capture the stream and check what precedes the zero length — earlier frames are likely being mis-read","Disconnect and let frp re-establish the vnet session; persistent occurrences indicate a real framing bug — report with logs"],"exampleFix":"// before: treat any ReadMessage failure identically, keep the conn\ndata, err := vnet.ReadMessage(r)\nif err != nil { log.Println(err); continue }\n\n// after: framing violations are unrecoverable — drop the connection\ndata, err := vnet.ReadMessage(r)\nif err != nil {\n    log.Printf(\"vnet framing error, closing conn: %v\", err)\n    conn.Close()\n    return\n}","handlingStrategy":"try-catch","validationCode":"// only your own framed peers should reach a vnet stream; firewall the port\n// and authenticate before exchanging framed messages","typeGuard":null,"tryCatchPattern":"_, err := vnet.ReadMessage(r)\nif err != nil {\n    // 'message length is 0' means stream desync or junk input: close, never continue\n    conn.Close()\n    return\n}","preventionTips":["Never continue reading a stream after a framing violation","Keep vnet-related listeners off public interfaces","Align frpc/frps versions; framing is version-coupled"],"tags":["vnet","protocol","validation","security"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}