nats-io/nats-server · warning

unsupported PROXY protocol version: %d

Error message

unsupported PROXY protocol version: %d

What it means

This error is returned from the default branch of readProxyProtoHeader's version switch when detectProxyProtoVersion reports a version that is neither 1 nor 2. In the current implementation detectProxyProtoVersion only returns 1, 2, or an error, so this branch is a defensive guard for future/unknown protocol versions rather than a code path reachable through the public detection logic. It formats the detected version number into the message for diagnostics.

Source

Thrown at server/client_proxyproto.go:282

		}

		// Verify full signature
		fullSig := string(firstBytes) + string(remaining)
		if fullSig != proxyProtoV2Sig {
			return nil, nil, fmt.Errorf("%w: invalid signature", errProxyProtoInvalid)
		}

		// Read rest of header: ver/cmd, fam/proto, addr-len (4 bytes)
		header := make([]byte, 4)
		if _, err := io.ReadFull(conn, header); err != nil {
			return nil, nil, fmt.Errorf("failed to read v2 header: %w", err)
		}

		// Continue with parsing
		addr, err := parseProxyProtoV2Header(conn, header)
		return addr, nil, err
	default:
		return nil, nil, fmt.Errorf("unsupported PROXY protocol version: %d", version)
	}
}

// readProxyProtoV2Header is kept for backward compatibility and direct testing.
// It reads and parses a PROXY protocol v2 header from the connection.
// If the command is LOCAL (health check), it returns nil for addr and no error.
// If the command is PROXY, it returns the parsed address information.
// The connection must be fresh (no data read yet).
func readProxyProtoV2Header(conn net.Conn) (*proxyProtoAddr, error) {
	// Set read deadline to prevent hanging on slow/malicious clients
	if err := conn.SetReadDeadline(time.Now().Add(proxyProtoReadTimeout)); err != nil {
		return nil, err
	}
	defer conn.SetReadDeadline(time.Time{})

	// Read fixed header (16 bytes)
	header := make([]byte, proxyProtoV2HeaderSize)
	if _, err := io.ReadFull(conn, header); err != nil {

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Confirm the sender's PROXY protocol version; only v1 (text 'PROXY ...') and v2 (binary signature) are supported by this parser.
  2. If you modified detectProxyProtoVersion, add a corresponding case to the switch in readProxyProtoHeader or map the new version onto the existing v1/v2 parsers.
  3. Upstream: this is defensive code; if you see it in production without local modifications, capture the connection bytes and report the sender's protocol output.

Example fix

// before: detectProxyProtoVersion returns 3 for an experimental format
// after: return errProxyProtoUnrecognized for unknown versions so the
// unrecognized-format path (with byte replay) handles it instead
// if sig is neither v1 prefix nor v2 sig prefix { return 0, header, errProxyProtoUnrecognized }
Defensive patterns

Strategy: fallback

Type guard

func isUnsupportedVersion(err error) bool {
	return err != nil && strings.HasPrefix(err.Error(), "unsupported PROXY protocol version:")
}

Try / catch

addr, extra, err := readProxyProtoHeader(conn)
if err != nil {
	if isUnsupportedVersion(err) {
		// log version number from message; fall back to non-PROXY handling or reject
		conn.Close()
		return
	}
	return
}

Prevention

When it happens

Trigger: Only reachable if detectProxyProtoVersion is changed or extended to return a version other than 1 or 2 without readProxyProtoHeader being updated; the corresponding test (TestClientProxyProtoV1ParseUnknown et al.) exercises the surrounding switch but this branch itself would require a hypothetical version like 3.

Common situations: Future PROXY protocol revisions or custom forks emitting a new version byte; custom modifications to detectProxyProtoVersion; third-party code vendoring the parser and adding a version case upstream.

Related errors


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