nats-io/nats-server · error
unknown PROXY protocol command: 0x%02x
Error message
unknown PROXY protocol command: 0x%02x
What it means
The PROXY protocol v2 spec defines only two commands: LOCAL (0x0) and PROXY (0x1). This error is raised when the command nibble of the ver/cmd byte holds any other value, meaning the sender produced a header the library does not recognize. It is returned as-is (not wrapped in a sentinel), so callers must string-match rather than errors.Is.
Source
Thrown at server/client_proxyproto.go:350
// Parse address length (big-endian uint16)
addrLen := binary.BigEndian.Uint16(header[2:4])
// Handle LOCAL command (health check)
if command == proxyProtoCmdLocal {
// For LOCAL, we should skip the address data if any
if addrLen > 0 {
// Discard the address data
if _, err := io.CopyN(io.Discard, conn, int64(addrLen)); err != nil {
return nil, fmt.Errorf("failed to discard LOCAL command address data: %w", err)
}
}
return nil, nil // nil addr indicates LOCAL command
}
// Handle PROXY command
if command != proxyProtoCmdProxy {
return nil, fmt.Errorf("unknown PROXY protocol command: 0x%02x", command)
}
// Validate protocol (we only support STREAM/TCP)
if protocol != proxyProtoProtoStream {
return nil, fmt.Errorf("%w: only STREAM protocol supported", errProxyProtoUnsupported)
}
// Parse address data based on family
var addr *proxyProtoAddr
var err error
switch family {
case proxyProtoFamilyInet:
addr, err = parseIPv4Addr(conn, addrLen)
case proxyProtoFamilyInet6:
addr, err = parseIPv6Addr(conn, addrLen)
case proxyProtoFamilyUnspec:
// UNSPEC family with PROXY command is valid but rare
// Just skip the address dataView on GitHub (pinned to 3a66a489d2)
Solutions
- Fix or update the upstream sender to emit only LOCAL (0x0) or PROXY (0x1) commands
- Dump the ver/cmd byte to identify which value is being sent
- Check the proxy vendor's documentation for nonstandard command extensions
- If the command is legitimate per a newer spec revision, patch parseProxyProtoV2Header to handle it
Example fix
// before (sender) verCmd := 0x22 // command 0x2 unsupported // after verCmd := 0x21 // command PROXY (0x1)
Defensive patterns
Strategy: try-catch
Validate before calling
// Only 0x0 (LOCAL) and 0x1 (PROXY) are valid command nibbles
if c := hdr[12]&0x0F; c > 0x1 {
return fmt.Errorf("invalid PROXY v2 command 0x%x", c)
} Type guard
func isKnownProxyCmd(b byte) bool { c := b&0x0F; return c == 0x0 || c == 0x1 } Try / catch
addr, err := readProxyProtoHeader(conn)
if err != nil {
if strings.HasPrefix(err.Error(), "unknown PROXY protocol command") {
// log ver/cmd byte, close connection — sender is non-conformant
conn.Close()
return
}
return err
} Prevention
- Audit senders to emit only LOCAL (0x0) or PROXY (0x1) command nibbles
- Fuzz-test the parser with random command values in staging
- Log the raw ver/cmd byte when this fires to identify the offending sender
- Check vendor docs for nonstandard command extensions before enabling
When it happens
Trigger: A peer sends a valid v2 signature but the ver/cmd low nibble is >= 0x2, e.g. command bits 0x2-0xF from a buggy sender, bit-flipped corruption, or a custom/nonstandard sender.
Common situations: Firmware bugs in proxies emitting reserved command values; memory/bit corruption on the wire; handcrafted clients testing the parser; protocol extensions the server has not implemented.
Related errors
- failed to read protocol version: %w
- failed to read v1 line: %w
- %w: v1 line too long
- %w: invalid v1 format
- %w: invalid address
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/b0845b51eaf28c08.
Report an issue: GitHub.