{"record":{"id":"141e37a71e3b2809","repo":"nats-io/nats-server","slug":"failed-to-read-protocol-version-w","errorCode":null,"errorMessage":"failed to read protocol version: %w","messagePattern":"failed to read protocol version: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/client_proxyproto.go","lineNumber":120,"sourceCode":"// extracted from the PROXY protocol header\ntype proxyConn struct {\n\tnet.Conn\n\tremoteAddr net.Addr\n}\n\n// RemoteAddr returns the original client address extracted from PROXY protocol\nfunc (pc *proxyConn) RemoteAddr() net.Addr {\n\treturn pc.remoteAddr\n}\n\n// detectProxyProtoVersion reads the first bytes and determines protocol version.\n// Returns 1 for v1, 2 for v2, or error.\n// The first 6 bytes read are returned so they can be used by the parser.\nfunc detectProxyProtoVersion(conn net.Conn) (version int, header []byte, err error) {\n\t// Read first 6 bytes to check for \"PROXY \" or v2 signature\n\theader = make([]byte, 6)\n\tif _, err = io.ReadFull(conn, header); err != nil {\n\t\treturn 0, nil, fmt.Errorf(\"failed to read protocol version: %w\", err)\n\t}\n\tswitch bytesToString(header) {\n\tcase proxyProtoV1Prefix:\n\t\treturn 1, header, nil\n\tcase proxyProtoV2Sig[:6]:\n\t\treturn 2, header, nil\n\tdefault:\n\t\t// Return the consumed bytes so the caller can replay them into the\n\t\t// next protocol layer instead of discarding them.\n\t\treturn 0, header, errProxyProtoUnrecognized\n\t}\n}\n\n// readProxyProtoV1Header parses PROXY protocol v1 text format.\n// Expects the \"PROXY \" prefix (6 bytes) to have already been consumed.\n// Returns any bytes that were read past the trailing CRLF so the caller can\n// replay them into the next protocol layer.\nfunc readProxyProtoV1Header(conn net.Conn) (*proxyProtoAddr, []byte, error) {","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/client_proxyproto.go#L102-L138","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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)"],"exampleFix":"// before (no header sent)\nnc nats.example.com 9000\n// after (send PROXY v1 header first)\nprintf 'PROXY TCP4 10.0.0.1 10.0.0.2 50000 4222\\r\\n' | nc nats.example.com 9000","handlingStrategy":"fallback","validationCode":"// client side: only connect to a PROXY-protocol port if you will send a header\nif !sendProxyHeader {\n    port = plainPort // e.g. 4222 instead of the proxy-protocol listener\n}","typeGuard":null,"tryCatchPattern":"conn, err := dialer.Dial(\"tcp\", addr)\nif err != nil {\n    // retry with backoff; the server may only accept PROXY-protocol peers\n    return retryDial(addr)\n}","preventionTips":["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"],"tags":["network","proxy-protocol","tcp"],"backgroundTag":"proxy-protocol-header-read-failed","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}