{"record":{"id":"36fd6908e128fc69","repo":"VictoriaMetrics/VictoriaMetrics","slug":"cannot-read-ipv6-address-from-proxy-protocol-block","errorCode":null,"errorMessage":"cannot read ipv6 address from proxy protocol block with the length %d bytes; expected at least 36 bytes","messagePattern":"cannot read ipv6 address from proxy protocol block with the length (.+?) bytes; expected at least 36 bytes","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/netutil/proxyprotocol.go","lineNumber":129,"sourceCode":"\t\t// Proxy LOCAL command. Ignore the protocol block. The real sender address should be used.\n\t\treturn nil, nil\n\tcase 1:\n\t\t// Parse the protocol block according to the family.\n\t\tswitch family {\n\t\tcase 1:\n\t\t\t// ipv4 (aka AF_INET)\n\t\t\tif len(bb.B) < 12 {\n\t\t\t\treturn nil, fmt.Errorf(\"cannot read ipv4 address from proxy protocol block with the length %d bytes; expected at least 12 bytes\", len(bb.B))\n\t\t\t}\n\t\t\tremoteAddr := &net.TCPAddr{\n\t\t\t\tIP:   net.IPv4(bb.B[0], bb.B[1], bb.B[2], bb.B[3]),\n\t\t\t\tPort: int(binary.BigEndian.Uint16(bb.B[8:10])),\n\t\t\t}\n\t\t\treturn remoteAddr, nil\n\t\tcase 2:\n\t\t\t// ipv6 (aka AF_INET6)\n\t\t\tif len(bb.B) < 36 {\n\t\t\t\treturn nil, fmt.Errorf(\"cannot read ipv6 address from proxy protocol block with the length %d bytes; expected at least 36 bytes\", len(bb.B))\n\t\t\t}\n\t\t\tvar ipv6Addr net.IP\n\t\t\tipv6Addr = append(ipv6Addr, bb.B[:16]...)\n\t\t\tremoteAddr := &net.TCPAddr{\n\t\t\t\tIP:   ipv6Addr,\n\t\t\t\tPort: int(binary.BigEndian.Uint16(bb.B[32:34])),\n\t\t\t}\n\t\t\treturn remoteAddr, nil\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"the proxy protocol implementation doesn't support protocol family %d; supported values: 1, 2\", family)\n\t\t}\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"the proxy protocol implementation doesn't support command %d; supported values: 0, 1\", command)\n\t}\n}\n\nconst v2Identifier = \"\\r\\n\\r\\n\\x00\\r\\nQUIT\\n\"\n","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/VictoriaMetrics/VictoriaMetrics/blob/5079fb58f1e8e62113f90c945ad71586c797d770/lib/netutil/proxyprotocol.go#L111-L147","documentation":"The proxy protocol v2 header declared a PROXY command with an IPv6 (AF_INET6) address block, but the block payload received was shorter than the 36 bytes needed for a 16-byte IPv6 address + 16-byte destination address + 2+2 bytes of ports. The library throws this because parsing the block would read past its end, so the sender's header length field does not match the actual address data sent.","triggerScenarios":"A client/load balancer sends a proxy protocol v2 header with version/command byte indicating command=1 (PROXY) and family nibble=2 (IPv6), but the length field at bytes 14-15 (blockLen) is under 36 or the peer closes/sends fewer bytes, so io.ReadFull returns a short block and len(bb.B) < 36 in readProxyProto (lib/netutil/proxyprotocol.go:128-129).","commonSituations":"A misconfigured HAProxy/NGINX/AWS NLB sending truncated or malformed v2 binary headers; a proxy sending v1 (text) header fragments that get misframed; writing a custom proxy-protocol sender that sets an incorrect payload length; network truncation between proxy and VictoriaMetrics listener with -proxyProtocolAddr enabled.","solutions":["Fix the upstream proxy so it emits a correct proxy protocol v2 header: for AF_INET6 the address block must be 36 bytes (16 src addr + 16 dst addr + 2 src port + 2 dst port) and the header length field must equal the block size plus any TLVs.","Verify the sender is actually speaking binary v2 (starts with the 12-byte signature \\r\\n\\r\\n\\x00\\r\\nQUIT\\n), not v1 text ('PROXY TCP6 ...') mixed into a v2 stream.","Capture the first bytes the client sends (tcpdump or the logged header %q in the sibling error) to confirm which proxy is misconfigured.","If the sender only sends health-check LOCAL commands, ensure it uses command=0 (proto 0), which skips block parsing entirely and cannot hit this error."],"exampleFix":"// before (misconfigured HAProxy v2 sender emits a 16-byte block for IPv6)\n// signature ... cmd=0x21, fam=0x22, len=0x0010  -> too short\n// after\n// signature ... cmd=0x21, fam=0x22, len=0x0024  -> 36-byte IPv6 address block","handlingStrategy":"validation","validationCode":"// Before sending (sender side) or testing (receiver side), assert the v2 address block size:\nfunc validV2IPv6Block(block []byte) bool { return len(block) >= 36 }\n// Sender: hdr length field must equal len(block):\n// binary.BigEndian.PutUint16(hdr[14:16], uint16(len(block))) // 36 for AF_INET6","typeGuard":"func isSupportedProxyFamily(family byte) bool { return family == 1 || family == 2 }\nfunc hasIPv6ProxyBlock(block []byte) bool { return len(block) >= 36 }","tryCatchPattern":null,"preventionTips":["Always set the v2 header length field to the exact address-block size (36 bytes for IPv6, 12 for IPv4).","Use a maintained proxy-protocol library (e.g. github.com/haproxytech/proxy-protocol or officem oplib) instead of hand-packing headers.","Integration-test with real HAProxy/NGINX send-proxy-v2 before production.","Monitor VictoriaMetrics logs for 'proxy protocol' errors after load balancer config changes."],"tags":["go","network","proxy-protocol","ipv6"],"backgroundTag":"proxy-protocol-malformed-header","analyzedSha":"5079fb58f1e8e62113f90c945ad71586c797d770","analyzedAt":"2026-09-03T18:10:26.153Z","contentChangedAt":"2026-09-03T18:10:26.153Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}