{"record":{"id":"27c501eef60a3bb6","repo":"nats-io/nats-server","slug":"w-tcp6-with-ipv4-address","errorCode":null,"errorMessage":"%w: TCP6 with IPv4 address","messagePattern":"%w: TCP6 with IPv4 address","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/client_proxyproto.go","lineNumber":219,"sourceCode":"\t}\n\n\tdstPort, err := strconv.ParseUint(parts[4], 10, 16)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"invalid dest port: %w\", err)\n\t}\n\n\t// Validate protocol matches IP version. The textual form determines the\n\t// family: TCP4 requires dotted-quad addresses, TCP6 requires IPv6\n\t// addresses. IPv4-mapped IPv6 addresses (e.g. \"::ffff:192.0.2.1\") are\n\t// valid for TCP6 since dual-stack proxies can emit those for IPv4\n\t// clients on IPv6 sockets, matching the v2 parser behavior.\n\tsrcIsV6 := strings.Contains(parts[1], \":\")\n\tdstIsV6 := strings.Contains(parts[2], \":\")\n\tif protocol == proxyProtoV1TCP4 && (srcIsV6 || dstIsV6) {\n\t\treturn nil, nil, fmt.Errorf(\"%w: TCP4 with IPv6 address\", errProxyProtoInvalid)\n\t}\n\tif protocol == proxyProtoV1TCP6 && (!srcIsV6 || !dstIsV6) {\n\t\treturn nil, nil, fmt.Errorf(\"%w: TCP6 with IPv4 address\", errProxyProtoInvalid)\n\t}\n\tif protocol != proxyProtoV1TCP4 && protocol != proxyProtoV1TCP6 {\n\t\treturn nil, nil, fmt.Errorf(\"%w: invalid protocol %s\", errProxyProtoInvalid, protocol)\n\t}\n\n\treturn &proxyProtoAddr{\n\t\tsrcIP:   srcIP,\n\t\tsrcPort: uint16(srcPort),\n\t\tdstIP:   dstIP,\n\t\tdstPort: uint16(dstPort),\n\t}, remaining, nil\n}\n\n// readProxyProtoHeader reads and parses PROXY protocol (v1 or v2) from the connection.\n// Automatically detects version and routes to appropriate parser.\n// If the command is LOCAL/UNKNOWN (health check), it returns nil for addr and no error.\n// If the command is PROXY, it returns the parsed address information.\n// It also returns any bytes that were read past the v1 header terminator so the","sourceCodeStart":201,"sourceCodeEnd":237,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/client_proxyproto.go#L201-L237","documentation":"The header declared protocol TCP6 but at least one address field lacks a colon, i.e. it is an IPv4 dotted-quad literal. TCP6 requires IPv6 literals for both source and destination, so the server rejects the header with errProxyProtoInvalid. (IPv4-mapped forms like '::ffff:192.0.2.1' do count as IPv6 for TCP6.)","triggerScenarios":"Header like 'PROXY TCP6 192.0.2.1 198.51.100.7 35646 4222\\r\\n', or mixed 'PROXY TCP6 ::1 192.0.2.1 35646 4222\\r\\n'; triggered when either parts[1] or parts[2] contains no ':'.","commonSituations":"Proxy hardcoding TCP6 while connected over IPv4; dual-stack misconfiguration; templates copied from an IPv6 deployment but running on IPv4 sockets.","solutions":["Make the proxy emit TCP4 when addresses are dotted-quad IPv4","Fix the proxy's protocol keyword to match the actual address family of the socket","If IPv4 clients arrive on IPv6 sockets, use TCP6 with IPv4-mapped literals ('::ffff:192.0.2.1') or switch to TCP4 with plain IPv4","Audit load-balancer proxy-protocol templates for hardcoded TCP6"],"exampleFix":"// before\n\"PROXY TCP6 192.0.2.1 198.51.100.7 35646 4222\\r\\n\"\n// after\n\"PROXY TCP4 192.0.2.1 198.51.100.7 35646 4222\\r\\n\"","handlingStrategy":"validation","validationCode":"proto := \"TCP4\"\nif strings.Contains(srcIPStr, \":\") && strings.Contains(dstIPStr, \":\") {\n    proto = \"TCP6\"\n}\nheader := fmt.Sprintf(\"PROXY %s %s %s %d %d\\r\\n\", proto, srcIPStr, dstIPStr, sport, dport)","typeGuard":"func protoMatchesFamily(proto, src, dst string) bool {\n    srcV6, dstV6 := strings.Contains(src, \":\"), strings.Contains(dst, \":\")\n    if proto == \"TCP6\" { return srcV6 && dstV6 }\n    if proto == \"TCP4\" { return !srcV6 && !dstV6 }\n    return false\n}","tryCatchPattern":"_, _, err := readProxyProtoHeader(conn)\nif err != nil {\n    if errors.Is(err, errProxyProtoInvalid) && strings.Contains(err.Error(), \"TCP6 with IPv4\") {\n        log.Printf(\"peer sent TCP6 with IPv4 address: %v\", err)\n        return\n    }\n    return err\n}","preventionTips":["Derive the protocol keyword from the actual socket family, not a static config","For IPv4 connections use TCP4 with dotted-quad addresses","Remember IPv4-mapped IPv6 literals count as IPv6 for TCP6"],"tags":["proxy-protocol","network","ipv6","nats"],"backgroundTag":"proxy-protocol-invalid-header","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}