nats-io/nats-server · error
failed to read v2 signature: %w
Error message
failed to read v2 signature: %w
What it means
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.
Source
Thrown at server/client_proxyproto.go:263
defer conn.SetReadDeadline(time.Time{})
// Detect version.
// On errProxyProtoUnrecognized, firstBytes holds the bytes that were
// consumed so the caller can replay them.
version, firstBytes, err := detectProxyProtoVersion(conn)
if err != nil {
return nil, firstBytes, err
}
switch version {
case 1:
// v1 parser expects "PROXY " prefix already consumed
return readProxyProtoV1Header(conn)
case 2:
// Read rest of v2 signature (bytes 6-11, total 6 more bytes)
remaining := make([]byte, 6)
if _, err := io.ReadFull(conn, remaining); err != nil {
return nil, nil, fmt.Errorf("failed to read v2 signature: %w", err)
}
// 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:View on GitHub (pinned to 3a66a489d2)
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
Example fix
// before: partial signature write then close
conn.Write([]byte("\x0D\x0A\x0D\x0A\x00\x0D"))
conn.Close()
// after: write full 12-byte signature + header
sig := "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"
conn.Write([]byte(sig + "\x20\x11\x00\x0C" /* + address bytes */)) Defensive patterns
Strategy: try-catch
Validate before calling
// Sender: write the entire v2 header (signature + header + addr) in one Write:
buf := append([]byte(sig12Bytes), hdrAndAddr...)
if _, err := conn.Write(buf); err != nil { return err } Type guard
func isTimeoutErr(err error) bool {
var ne net.Error
return errors.As(err, &ne) && ne.Timeout()
} Try / catch
addr, _, err := readProxyProtoHeader(conn)
if err != nil {
if strings.Contains(err.Error(), "failed to read v2 signature") {
if isTimeoutErr(err) {
log.Printf("peer stalled while sending v2 signature (5s deadline): %v", err)
} else if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
log.Printf("peer closed mid v2 signature: %v", err)
}
return
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- failed to read protocol version: %w
- failed to read v1 line: %w
- %w: v1 line too long
- failed to read v2 header: %w
- failed to read PROXY protocol header: %w
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/6a25c222a53e8acc.
Report an issue: GitHub.