nats-io/nats-server · error
failed to read protocol version: %w
Error message
failed to read protocol version: %w
What it means
During PROXY protocol detection the server could not read the first 6 bytes of the connection (io.ReadFull failed). Those bytes are needed to distinguish PROXY v1 ('PROXY ') from v2 (binary signature), so detection fails and the wrapped underlying I/O error is returned.
Source
Thrown at server/client_proxyproto.go:120
// extracted from the PROXY protocol header
type proxyConn struct {
net.Conn
remoteAddr net.Addr
}
// RemoteAddr returns the original client address extracted from PROXY protocol
func (pc *proxyConn) RemoteAddr() net.Addr {
return pc.remoteAddr
}
// detectProxyProtoVersion reads the first bytes and determines protocol version.
// Returns 1 for v1, 2 for v2, or error.
// The first 6 bytes read are returned so they can be used by the parser.
func detectProxyProtoVersion(conn net.Conn) (version int, header []byte, err error) {
// Read first 6 bytes to check for "PROXY " or v2 signature
header = make([]byte, 6)
if _, err = io.ReadFull(conn, header); err != nil {
return 0, nil, fmt.Errorf("failed to read protocol version: %w", err)
}
switch bytesToString(header) {
case proxyProtoV1Prefix:
return 1, header, nil
case proxyProtoV2Sig[:6]:
return 2, header, nil
default:
// Return the consumed bytes so the caller can replay them into the
// next protocol layer instead of discarding them.
return 0, header, errProxyProtoUnrecognized
}
}
// readProxyProtoV1Header parses PROXY protocol v1 text format.
// Expects the "PROXY " prefix (6 bytes) to have already been consumed.
// Returns any bytes that were read past the trailing CRLF so the caller can
// replay them into the next protocol layer.
func readProxyProtoV1Header(conn net.Conn) (*proxyProtoAddr, []byte, error) {View on GitHub (pinned to 3a66a489d2)
Solutions
- Ensure the upstream proxy actually sends a PROXY protocol v1 or v2 header on every connection
- Verify the client connects to the correct port (non-PROXY port if it does not send a proxy header)
- Check for aggressive health checks/scans closing connections early; point them at a plain monitoring port
- Investigate network stability/timeout between the proxy and the server (the wrapped error shows the root cause)
Example fix
// before (no header sent) nc nats.example.com 9000 // after (send PROXY v1 header first) printf 'PROXY TCP4 10.0.0.1 10.0.0.2 50000 4222\r\n' | nc nats.example.com 9000
Defensive patterns
Strategy: fallback
Validate before calling
// client side: only connect to a PROXY-protocol port if you will send a header
if !sendProxyHeader {
port = plainPort // e.g. 4222 instead of the proxy-protocol listener
} Try / catch
conn, err := dialer.Dial("tcp", addr)
if err != nil {
// retry with backoff; the server may only accept PROXY-protocol peers
return retryDial(addr)
} Prevention
- Configure health checks to hit a non-PROXY-protocol port or keep the connection alive
- Make sure every upstream proxy sends a complete PROXY header on connect
- Do not point plain (non-proxy-header) clients at a proxy_protocol listener
- Log the wrapped cause (%w) to distinguish timeout vs reset vs EOF
When it happens
Trigger: A client connects to a PROXY-protocol-enabled listener but closes immediately, times out, or sends fewer than 6 bytes; network interruption between proxy and server before the header arrives.
Common situations: Health-check probes that open and close TCP connections, monitoring port scans, a proxy configured for PROXY protocol not actually sending the header, TLS clients connecting to a PROXY-protocol port (TLS bytes < 6 or read stalls).
Related errors
- failed to read v1 line: %w
- %w: v1 line too long
- failed to read v2 signature: %w
- %w: invalid v1 format
- %w: invalid address
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/141e37a71e3b2809.
Report an issue: GitHub.