nats-io/nats-server · error

invalid source port: %w

Error message

invalid source port: %w

What it means

The v1 header's source-port field could not be parsed by strconv.ParseUint base 10 with bit size 16. The field was empty, non-numeric, or outside 0-65535. The server wraps the strconv error and aborts the connection.

Source

Thrown at server/client_proxyproto.go:200

	}

	// 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.
	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) {

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Ensure the source-port field is a decimal number in 0-65535
  2. Check the proxy template for unfilled/interpolated port variables
  3. Inspect the raw header bytes to identify what text landed in the port field
  4. Fix test clients to format ports with %d, not strings

Example fix

// before
"PROXY TCP4 192.0.2.1 198.51.100.7 abc 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

sport, err := strconv.ParseUint(srcPortStr, 10, 16)
if err != nil {
    return fmt.Errorf("refusing to send PROXY header: bad source port %q", srcPortStr)
}

Type guard

func validPort(s string) bool {
    p, err := strconv.ParseUint(s, 10, 16)
    return err == nil && p > 0
}

Try / catch

_, _, err := readProxyProtoHeader(conn)
if err != nil {
    if strings.Contains(err.Error(), "invalid source port") {
        log.Printf("peer sent bad PROXY src port: %v", err)
        return
    }
    return err
}

Prevention

When it happens

Trigger: Header contains a bad source port such as 'PROXY TCP4 192.0.2.1 198.51.100.7 abc 4222\r\n', a negative port, or a port above 65535 like '70000'.

Common situations: Proxy emitting port placeholders or variable interpolation failures (e.g. empty template slot); corrupted stream mixing header bytes; hand-written client using hex or string port values.

Related errors


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