nats-io/nats-server · warning
failed to read IPv4 address data: %w
Error message
failed to read IPv4 address data: %w
What it means
The v2 header declared a valid (>=12 byte) IPv4 address length, but reading that many bytes from the connection with io.ReadFull failed (connection closed, timeout, or reset) before the full address payload arrived. The underlying I/O error is wrapped in the %w so the cause is preserved.
Source
Thrown at server/client_proxyproto.go:389
return nil, fmt.Errorf("failed to discard UNSPEC address address data: %w", err)
}
}
return nil, nil
default:
return nil, fmt.Errorf("%w: unsupported address family 0x%02x", errProxyProtoUnsupported, family)
}
return addr, err
}
// parseIPv4Addr parses IPv4 address data from PROXY protocol header
func parseIPv4Addr(conn net.Conn, addrLen uint16) (*proxyProtoAddr, error) {
// IPv4: 4 (src IP) + 4 (dst IP) + 2 (src port) + 2 (dst port) = 12 bytes minimum
if addrLen < proxyProtoAddrSizeIPv4 {
return nil, fmt.Errorf("IPv4 address data too short: %d bytes", addrLen)
}
addrData := make([]byte, addrLen)
if _, err := io.ReadFull(conn, addrData); err != nil {
return nil, fmt.Errorf("failed to read IPv4 address data: %w", err)
}
return &proxyProtoAddr{
srcIP: net.IP(addrData[0:4]),
dstIP: net.IP(addrData[4:8]),
srcPort: binary.BigEndian.Uint16(addrData[8:10]),
dstPort: binary.BigEndian.Uint16(addrData[10:12]),
}, nil
}
// parseIPv6Addr parses IPv6 address data from PROXY protocol header
func parseIPv6Addr(conn net.Conn, addrLen uint16) (*proxyProtoAddr, error) {
// IPv6: 16 (src IP) + 16 (dst IP) + 2 (src port) + 2 (dst port) = 36 bytes minimum
if addrLen < proxyProtoAddrSizeIPv6 {
return nil, fmt.Errorf("IPv6 address data too short: %d bytes", addrLen)
}
addrData := make([]byte, addrLen)
if _, err := io.ReadFull(conn, addrData); err != nil {
return nil, fmt.Errorf("failed to read IPv6 address data: %w", err)View on GitHub (pinned to 3a66a489d2)
Solutions
- Ensure the upstream proxy writes the complete PROXY header atomically (single write) so it is never split by disconnects.
- Check whether clients/probes are connecting to a PROXY-enabled listener without speaking PROXY protocol and move them to a non-PROXY listener.
- Handle this as a transient connection error: retry the connection; use errors.Is/As on the wrapped cause to distinguish io.EOF/io.ErrUnexpectedEOF from timeouts.
Example fix
// before: partial writes from proxy tcpConn.Write(headerSig) // peer may disconnect here // after buf := buildFullV2Header(addr) tcpConn.Write(buf) // one atomic write
Defensive patterns
Strategy: retry
Type guard
func isTransientHeaderRead(err error) bool {
return errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, os.ErrDeadlineExceeded)
} Try / catch
_, err := connectWithProxy()
if err != nil && strings.Contains(err.Error(), "failed to read IPv4 address data") {
if isTransientHeaderRead(errors.Unwrap(err)) {
time.Sleep(backoff)
return connectWithProxy() // retry transient disconnect
}
} Prevention
- Send the entire PROXY header in one write() from the proxy
- Exclude health-check probes from PROXY-enabled ports
- Keep connect timeouts on the proxy path generous enough for the full header exchange
When it happens
Trigger: Client disconnects immediately after sending the PROXY header signature and length but before the 12-byte address data; network timeout mid-header; LB health-check probes that send a partial header then close.
Common situations: Health checks / port scanners hitting a PROXY-enabled port with half-formed headers; flaky mobile clients; LB connection draining between header write and body write.
Related errors
- failed to read v2 signature: %w
- failed to read v2 header: %w
- failed to read PROXY protocol header: %w
- failed to discard LOCAL command address data: %w
- failed to discard UNSPEC address address data: %w
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/8891b372453ee245.
Report an issue: GitHub.