{"record":{"id":"bf9725000721bb2c","repo":"nats-io/nats-server","slug":"message-dynamic-websocket-protocol-error-sent","errorCode":null,"errorMessage":"${message} (dynamic websocket protocol error, sent to client as close status 1002)","messagePattern":"(.+?) \\(dynamic websocket protocol error, sent to client as close status 1002\\)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/websocket.go","lineNumber":776,"sourceCode":"\t\t// status in the close message. So using this one instead.\n\t\tstatus = wsCloseStatusGoingAway\n\tdefault:\n\t\tstatus = wsCloseStatusInternalSrvError\n\t}\n\tbody := wsCreateCloseMessage(status, reason.String())\n\tc.wsEnqueueControlMessageLocked(wsCloseMessage, body)\n\tnbPoolPut(body) // wsEnqueueControlMessageLocked has taken a copy.\n}\n\n// Create and then enqueue a close message with a protocol error and the\n// given message. This is invoked when parsing websocket frames.\n//\n// Lock MUST NOT be held on entry.\nfunc (c *client) wsHandleProtocolError(message string) error {\n\tbuf := wsCreateCloseMessage(wsCloseStatusProtocolError, message)\n\tc.wsEnqueueControlMessage(wsCloseMessage, buf)\n\tnbPoolPut(buf) // wsEnqueueControlMessage has taken a copy.\n\treturn errors.New(message)\n}\n\nfunc wsIsValidCloseStatus(code int) bool {\n\tswitch code {\n\tcase wsCloseStatusNoStatusReceived, 1004, 1006, wsCloseStatusTLSHandshake:\n\t\treturn false\n\t}\n\tif code < 1000 || code >= 5000 {\n\t\treturn false\n\t}\n\t// 1016-2999 are currently reserved.\n\tif code >= 1016 && code <= 2999 {\n\t\treturn false\n\t}\n\treturn true\n}\n\n// Create a close message with the given `status` and `body`.","sourceCodeStart":758,"sourceCodeEnd":794,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/websocket.go#L758-L794","documentation":"wsHandleProtocolError sends a WebSocket close frame with status 1002 (protocol error) containing the message to the client, then returns the message as a Go error so the server can close the connection. The message is labeled as a dynamic websocket protocol error surfaced to the client as close status 1002. It indicates the client (or server) violated the WebSocket protocol.","triggerScenarios":"Any code path in the server's websocket client handling that detects a protocol violation and calls c.wsHandleProtocolError(msg), e.g. malformed frames, invalid opcodes, bad control frames, or unsupported data during a websocket session.","commonSituations":"A non-NATS websocket client (browser dev tools, proxies, custom clients) sending malformed frames; intermediaries/proxies mangling websocket traffic; protocol negotiation bugs after server upgrades.","solutions":["Fix the client to send spec-compliant WebSocket frames (correct opcodes, masking, control-frame sizes)","Inspect the error message text — it is passed through as the close reason and names the exact protocol violation","Check for intermediaries (proxies, load balancers) that rewrite or buffer websocket frames and bypass them","Update client and server libraries to compatible versions"],"exampleFix":"// before: client sends a text frame where a binary frame is expected\nws.send(JSON.stringify(payload));\n// after\nws.send(new TextEncoder().encode(JSON.stringify(payload))); // binary per NATS websocket protocol","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// browser client\nws.onclose = (ev) => {\n  if (ev.code === 1002) {\n    console.error('websocket protocol error, server reason:', ev.reason);\n    // log client frame state and reconnect only after fixing the client frames\n  }\n};","preventionTips":["Use a maintained websocket client library instead of hand-rolled framing","Test clients against the server with wscat or official NATS clients","Audit proxies/LBs for websocket upgrade support and frame passthrough","Keep server and client protocol implementations on compatible versions"],"tags":["websocket","protocol-error","close-1002"],"backgroundTag":"websocket-protocol-error","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}