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
- Ensure clients use a NATS client library speaking the NATS protocol.
- Check for TLS/plaintext mismatch: enable TLS on the server side or connect with tls:// to plain ports correctly.
- Inspect the logged proto snippet to identify the sending client and the garbage bytes.
- 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
- Use official NATS client libraries, not hand-rolled protocol writers.
- Match TLS settings: don't send TLS to plaintext ports or vice versa.
- Don't point HTTP/other-protocol clients at NATS ports.
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
- bad message header detected
- topic filter cannot be empty
- malformed variable int
- received a second CONNECT packet
- connect flags reserved bit not set to 0
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/6780620255566aa0.
Report an issue: GitHub.