nats-io/nats-server · error

%w: invalid address

Error message

%w: invalid address

What it means

The v1 header's source or destination IP field failed net.ParseIP, meaning it is neither a valid IPv4 dotted-quad nor a valid IPv6 literal. The server rejects the header with an error wrapping errProxyProtoInvalid because the addresses cannot represent a real connection.

Source

Thrown at server/client_proxyproto.go:194

		return nil, nil, fmt.Errorf("%w: invalid v1 format", errProxyProtoInvalid)
	}

	// Handle UNKNOWN (health check, like v2 LOCAL)
	if parts[0] == proxyProtoV1Unknown {
		return nil, remaining, nil
	}

	// Must have exactly 5 parts: protocol, src-ip, dst-ip, src-port, dst-port
	if len(parts) != 5 {
		return nil, nil, fmt.Errorf("%w: invalid v1 format", errProxyProtoInvalid)
	}

	protocol := parts[0]
	srcIP := net.ParseIP(parts[1])
	dstIP := net.ParseIP(parts[2])

	if srcIP == nil || dstIP == nil {
		return nil, nil, fmt.Errorf("%w: invalid address", errProxyProtoInvalid)
	}

	// Parse ports
	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.

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Confirm the sender writes valid IPv4 dotted-quad or IPv6 literals in both address fields
  2. Fix proxy config that substitutes hostnames (e.g. 'localhost') instead of IPs
  3. Log/inspect the raw header (tcpdump) to see exactly which field is bad
  4. Update broken test fixtures to use valid addresses like 192.0.2.1 (TEST-NET)

Example fix

// before
"PROXY TCP4 localhost 198.51.100.7 35646 4222\r\n"
// after
"PROXY TCP4 192.0.2.1 198.51.100.7 35646 4222\r\n"
Defensive patterns

Strategy: validation

Validate before calling

src := net.ParseIP(srcIPStr)
dst := net.ParseIP(dstIPStr)
if src == nil || dst == nil {
    return fmt.Errorf("cannot send PROXY header: invalid IP %q / %q", srcIPStr, dstIPStr)
}

Type guard

func parseableIPs(parts []string) bool {
    return net.ParseIP(parts[1]) != nil && net.ParseIP(parts[2]) != nil
}

Try / catch

addr, _, err := readProxyProtoHeader(conn)
if err != nil {
    if errors.Is(err, errProxyProtoInvalid) && strings.Contains(err.Error(), "invalid address") {
        log.Printf("PROXY header had unparseable IP: %v", err)
        return
    }
    return err
}

Prevention

When it happens

Trigger: Header contains malformed IP text such as 'PROXY TCP4 999.1.1.1 198.51.100.7 35646 4222\r\n', hostnames instead of IPs, or empty address fields.

Common situations: Proxy template using hostname instead of resolved IP; misconfigured transparent proxy passing through garbage; double proxying where an inner header leaks; typo in a hand-written test fixture.

Related errors


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/93097637502e7ecf. Report an issue: GitHub.