nats-io/nats-server · error

%s parser ERROR, state=%d, i=%d: proto='%s...'

Error message

%s parser ERROR, state=%d, i=%d: proto='%s...'

What it means

The server's protocol parser hit an unknown protocol operation while in a given lexer/parser state. It builds an error with the connection kind, parser state, byte offset, and a snippet of the offending proto text via errorf(). Primarily exercised by parser tests and conf/fuzz.go's Fuzz entry, which feeds random data to Parse and expects errors like this.

Source

Thrown at server/parser.go:1255

			}
			c.msgBuf = make([]byte, lrem, c.pa.size+LEN_CR_LF)
			copy(c.msgBuf, buf[c.as:])
		} else {
			c.msgBuf = c.scratch[len(c.argBuf):len(c.argBuf)]
			c.msgBuf = append(c.msgBuf, (buf[c.as:])...)
		}
	}

	return nil

authErr:
	c.authViolation()
	return ErrAuthentication

parseErr:
	c.sendErr("Unknown Protocol Operation")
	snip := protoSnippet(i, PROTO_SNIPPET_SIZE, buf)
	err := fmt.Errorf("%s parser ERROR, state=%d, i=%d: proto='%s...'", c.kindString(), c.state, i, snip)
	return err
}

func protoSnippet(start, max int, buf []byte) string {
	stop := start + max
	bufSize := len(buf)
	if start >= bufSize {
		return `""`
	}
	if stop > bufSize {
		stop = bufSize - 1
	}
	return fmt.Sprintf("%q", buf[start:stop])
}

// Check if the length of buffer `arg` is over the max control line limit `mcl`.
// If so, an error is sent to the client and the connection is closed.
// The error ErrMaxControlLine is returned.

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Ensure clients use a NATS client library speaking the NATS protocol.
  2. Check for TLS/plaintext mismatch: enable TLS on the server side or connect with tls:// to plain ports correctly.
  3. Inspect the logged proto snippet to identify the sending client and the garbage bytes.
  4. If seen in fuzzing, minimize the input with the corpus entry that produced it.

Example fix

// before: connecting with raw HTTP to NATS port
curl http://localhost:4222/
// after: use a NATS client
nc, _ := nats.Connect("nats://localhost:4222")
Defensive patterns

Strategy: try-catch

Validate before calling

// Before connecting a custom client, ensure the first bytes are a known NATS op
if !regexp.MustCompile(`^(CONNECT|PUB|SUB|UNSUB|MSG|PING|PONG|INFO|PLUS|ERR)\b`).Match(buf) {
    return errors.New("not a NATS protocol operation")
}

Type guard

func isKnownProtoOp(b []byte) bool {
    for _, op := range []string{"CONNECT ", "PUB ", "SUB ", "UNSUB ", "PING", "PONG", "INFO ", "MSG ", "+OK", "-ERR"} {
        if bytes.HasPrefix(b, []byte(op)) { return true }
    }
    return false
}

Try / catch

if _, err := nats.Parse(string(data)); err != nil {
    // parse errors on garbage input are expected; log snippet and drop connection
    log.Printf("dropping client %s: %v", conn.RemoteAddr(), err)
    return
}

Prevention

When it happens

Trigger: A client (or fuzz corpus) sends bytes that do not match any known NATS protocol operation, causing the parser's parseErr path in server/parser.go.

Common situations: Non-NATS clients pointing at a NATS port, TLS traffic sent to a plaintext port (binary garbage), corrupted/framed payloads, or fuzz testing with malformed input.

Related errors


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