{"record":{"id":"6a25c222a53e8acc","repo":"nats-io/nats-server","slug":"failed-to-read-v2-signature-w","errorCode":null,"errorMessage":"failed to read v2 signature: %w","messagePattern":"failed to read v2 signature: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/client_proxyproto.go","lineNumber":263,"sourceCode":"\tdefer conn.SetReadDeadline(time.Time{})\n\n\t// Detect version.\n\t// On errProxyProtoUnrecognized, firstBytes holds the bytes that were\n\t// consumed so the caller can replay them.\n\tversion, firstBytes, err := detectProxyProtoVersion(conn)\n\tif err != nil {\n\t\treturn nil, firstBytes, err\n\t}\n\n\tswitch version {\n\tcase 1:\n\t\t// v1 parser expects \"PROXY \" prefix already consumed\n\t\treturn readProxyProtoV1Header(conn)\n\tcase 2:\n\t\t// Read rest of v2 signature (bytes 6-11, total 6 more bytes)\n\t\tremaining := make([]byte, 6)\n\t\tif _, err := io.ReadFull(conn, remaining); err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"failed to read v2 signature: %w\", err)\n\t\t}\n\n\t\t// Verify full signature\n\t\tfullSig := string(firstBytes) + string(remaining)\n\t\tif fullSig != proxyProtoV2Sig {\n\t\t\treturn nil, nil, fmt.Errorf(\"%w: invalid signature\", errProxyProtoInvalid)\n\t\t}\n\n\t\t// Read rest of header: ver/cmd, fam/proto, addr-len (4 bytes)\n\t\theader := make([]byte, 4)\n\t\tif _, err := io.ReadFull(conn, header); err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"failed to read v2 header: %w\", err)\n\t\t}\n\n\t\t// Continue with parsing\n\t\taddr, err := parseProxyProtoV2Header(conn, header)\n\t\treturn addr, nil, err\n\tdefault:","sourceCodeStart":245,"sourceCodeEnd":281,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/client_proxyproto.go#L245-L281","documentation":"After detecting the v2 signature's first 6 bytes, the server reads 6 more bytes to complete the 12-byte binary signature. If that read fails (connection closed early, timeout after 5s, or reset), the error wraps the underlying io error with this message. It indicates the sender started a v2 header but the bytes never arrived or the connection died mid-header.","triggerScenarios":"A client writes the first 6 signature bytes (\\r\\n\\r\\n\\x00\\r\\n) then closes or stalls before sending the remaining 6 bytes (QUIT\\n); network interruption between proxy and server; read deadline expiry during signature read.","commonSituations":"Proxy crashing mid-handshake; MTU/firewall truncating the header write; client sending only a partial signature to probe the port; slow health-checker hitting the 5-second read timeout.","solutions":["Verify the upstream proxy writes the full 12-byte v2 signature atomically","Check network stability and firewall/MTU settings between proxy and server for truncation","Look at the wrapped cause: io.EOF/ErrUnexpectedEOF means the sender closed early; i/o timeout means the sender stalled past 5s","Ensure health checkers either send a complete header or close cleanly without writing partial signatures"],"exampleFix":"// before: partial signature write then close\nconn.Write([]byte(\"\\x0D\\x0A\\x0D\\x0A\\x00\\x0D\"))\nconn.Close()\n// after: write full 12-byte signature + header\nsig := \"\\x0D\\x0A\\x0D\\x0A\\x00\\x0D\\x0A\\x51\\x55\\x49\\x54\\x0A\"\nconn.Write([]byte(sig + \"\\x20\\x11\\x00\\x0C\" /* + address bytes */))","handlingStrategy":"try-catch","validationCode":"// Sender: write the entire v2 header (signature + header + addr) in one Write:\nbuf := append([]byte(sig12Bytes), hdrAndAddr...)\nif _, err := conn.Write(buf); err != nil { return err }","typeGuard":"func isTimeoutErr(err error) bool {\n    var ne net.Error\n    return errors.As(err, &ne) && ne.Timeout()\n}","tryCatchPattern":"addr, _, err := readProxyProtoHeader(conn)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to read v2 signature\") {\n        if isTimeoutErr(err) {\n            log.Printf(\"peer stalled while sending v2 signature (5s deadline): %v\", err)\n        } else if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {\n            log.Printf(\"peer closed mid v2 signature: %v\", err)\n        }\n        return\n    }\n    return err\n}","preventionTips":["Write the full 12-byte v2 signature atomically in a single Write","Keep proxies and server on stable links; check for MTU/firewall truncation","Classify the wrapped cause (EOF vs timeout) to distinguish crash vs stall","Health checkers should send complete headers or connect-and-close without partial signatures"],"tags":["proxy-protocol","network","tcp","io"],"backgroundTag":"proxy-protocol-truncated-header","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}