nats-io/nats-server · error
%w: TCP4 with IPv6 address
Error message
%w: TCP4 with IPv6 address
What it means
The header declared protocol TCP4 but at least one of the address fields contains a colon, indicating an IPv6 literal. The PROXY protocol spec requires TCP4 to carry IPv4 dotted-quad addresses for both source and destination, so the server rejects the header with errProxyProtoInvalid.
Source
Thrown at server/client_proxyproto.go:216
srcPort, err := strconv.ParseUint(parts[3], 10, 16)
if err != nil {
return nil, nil, fmt.Errorf("invalid source port: %w", err)
}
dstPort, err := strconv.ParseUint(parts[4], 10, 16)
if err != nil {
return nil, nil, fmt.Errorf("invalid dest port: %w", err)
}
// Validate protocol matches IP version. The textual form determines the
// family: TCP4 requires dotted-quad addresses, TCP6 requires IPv6
// addresses. IPv4-mapped IPv6 addresses (e.g. "::ffff:192.0.2.1") are
// valid for TCP6 since dual-stack proxies can emit those for IPv4
// clients on IPv6 sockets, matching the v2 parser behavior.
srcIsV6 := strings.Contains(parts[1], ":")
dstIsV6 := strings.Contains(parts[2], ":")
if protocol == proxyProtoV1TCP4 && (srcIsV6 || dstIsV6) {
return nil, nil, fmt.Errorf("%w: TCP4 with IPv6 address", errProxyProtoInvalid)
}
if protocol == proxyProtoV1TCP6 && (!srcIsV6 || !dstIsV6) {
return nil, nil, fmt.Errorf("%w: TCP6 with IPv4 address", errProxyProtoInvalid)
}
if protocol != proxyProtoV1TCP4 && protocol != proxyProtoV1TCP6 {
return nil, nil, fmt.Errorf("%w: invalid protocol %s", errProxyProtoInvalid, protocol)
}
return &proxyProtoAddr{
srcIP: srcIP,
srcPort: uint16(srcPort),
dstIP: dstIP,
dstPort: uint16(dstPort),
}, remaining, nil
}
// readProxyProtoHeader reads and parses PROXY protocol (v1 or v2) from the connection.
// Automatically detects version and routes to appropriate parser.View on GitHub (pinned to 3a66a489d2)
Solutions
- Make the proxy emit TCP6 when addresses are IPv6 literals
- If the address is IPv4-mapped IPv6, use TCP6 (the server accepts '::ffff:x.x.x.x' for TCP6) or rewrite to a plain IPv4 literal with TCP4
- Fix the proxy's family-detection logic to derive the protocol keyword from the actual socket family
- Update hardcoded 'TCP4' templates used in dual-stack environments
Example fix
// before "PROXY TCP4 2001:db8::1 2001:db8::2 35646 4222\r\n" // after "PROXY TCP6 2001:db8::1 2001:db8::2 35646 4222\r\n"
Defensive patterns
Strategy: validation
Validate before calling
// Before sending, derive the keyword from the address family:
proto := "TCP4"
if strings.Contains(srcIPStr, ":") || strings.Contains(dstIPStr, ":") {
proto = "TCP6"
}
header := fmt.Sprintf("PROXY %s %s %s %d %d\r\n", proto, srcIPStr, dstIPStr, sport, dport) Type guard
func protoMatchesFamily(proto, src, dst string) bool {
srcV6, dstV6 := strings.Contains(src, ":"), strings.Contains(dst, ":")
if proto == "TCP4" { return !srcV6 && !dstV6 }
if proto == "TCP6" { return srcV6 && dstV6 }
return false
} Try / catch
_, _, err := readProxyProtoHeader(conn)
if err != nil {
if errors.Is(err, errProxyProtoInvalid) && strings.Contains(err.Error(), "TCP4 with IPv6") {
log.Printf("peer sent TCP4 with IPv6 address: %v", err)
return
}
return err
} Prevention
- Never hardcode TCP4 in dual-stack proxy templates
- Derive the protocol keyword from the socket's address family
- Use TCP6 with IPv4-mapped literals ('::ffff:x.x.x.x') for IPv4 clients on IPv6 sockets
When it happens
Trigger: Header like 'PROXY TCP4 2001:db8::1 198.51.100.7 35646 4222\r\n' (or with an IPv6 destination); any colon in either IP field while parts[0] == 'TCP4'.
Common situations: Dual-stack proxy hardcoding TCP4 while its sockets are IPv6; proxy behind an IPv6-only upstream; template not switching protocol keyword based on address family; NAT64/mapped-address setups emitting v6 literals under TCP4.
Related errors
- %w: TCP6 with IPv4 address
- %w: v1 line too long
- %w: invalid v1 format
- %w: invalid address
- invalid source port: %w
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/1716b1c38f8f191d.
Report an issue: GitHub.