nats-io/nats-server · error

failed to read v1 line: %w

Error message

failed to read v1 line: %w

What it means

After detecting PROXY protocol v1, the server reads the rest of the header line up to CRLF; conn.Read failed mid-read (timeout, reset, EOF), so the v1 header could not be completed and the wrapped I/O error is returned.

Source

Thrown at server/client_proxyproto.go:151

// 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) {
	// Read rest of line (max 107 bytes total, already read 6)
	maxRemaining := proxyProtoV1MaxLineLen - 6

	// Read up to maxRemaining bytes at once (more efficient than byte-by-byte)
	buf := make([]byte, maxRemaining)
	var line []byte
	var remaining []byte

	for len(line) < maxRemaining {
		// Read available data
		n, err := conn.Read(buf[len(line):])
		if err != nil {
			return nil, nil, fmt.Errorf("failed to read v1 line: %w", err)
		}

		line = buf[:len(line)+n]

		// Look for CRLF in what we've read so far
		for i := 0; i < len(line)-1; i++ {
			if line[i] == '\r' && line[i+1] == '\n' {
				// Found CRLF - keep any over-read bytes for the client parser.
				remaining = append(remaining, line[i+2:]...)
				line = line[:i]
				goto foundCRLF
			}
		}
	}

	// Exceeded max length without finding CRLF
	return nil, nil, fmt.Errorf("%w: v1 line too long", errProxyProtoInvalid)

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Fix the upstream proxy to send the complete single-line PROXY v1 header terminated by CRLF
  2. Check proxy health/stability and network path (resets, timeouts) between proxy and server
  3. Verify the proxy version supports PROXY protocol v1 and its header fits on one line under maxRemaining
  4. Reproduce with packet capture on the server port to see the truncated header bytes

Example fix

// before (truncated header)
PROXY TCP4 10.0.0.1
// after (complete v1 line with CRLF)
PROXY TCP4 10.0.0.1 10.0.0.2 50000 4222\r\n
Defensive patterns

Strategy: fallback

Validate before calling

// sender side: ensure full line + CRLF is written before flushing
header := fmt.Sprintf("PROXY TCP4 %s %s %d %d\r\n", srcIP, dstIP, srcPort, dstPort)
if n, err := conn.Write([]byte(header)); err != nil || n != len(header) {
    return fmt.Errorf("short proxy header write: %d/%d", n, len(header))
}

Try / catch

header, err := readProxyProtoHeader(conn)
if err != nil {
    // inspect wrapped cause; retry or fall back to direct connection
    return fmt.Errorf("proxy header failed: %w", err)
}

Prevention

When it happens

Trigger: A PROXY v1 sender writes fewer than 6 bytes then stalls, is killed, or the connection is reset before the full `PROXY ... CRLF` line arrives; also triggered when the line exceeds maxRemaining without CRLF causing continued reads to fail.

Common situations: Misconfigured load balancer sending a truncated PROXY v1 line, flaky network between proxy and NATS server, proxy crashed mid-handshake, MTU/buffer issues truncating the header.

Related errors


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/4c298d24a6fb9997. Report an issue: GitHub.