TooTallNate/Java-WebSocket · error · InvalidFrameException
bad rsv RSV1: RSV2: RSV3
Error message
bad rsv RSV1: {rsv1} RSV2: {rsv2} RSV3: {rsv3} What it means
CompressionExtension.isFrameValid rejects any DATA frame (text/binary/continuation) that has RSV2 or RSV3 bits set, since only RSV1 may be used for permessage-deflate compression. Setting reserved RSV bits without a negotiated extension is a WebSocket protocol violation, so an InvalidFrameException (close code 1002, PROTOCOL_ERROR) is thrown. The message reports the state of all three RSV bits.
Solutions
- Fix the peer to only set RSV1 for compressed frames under permessage-deflate and leave RSV2/RSV3 = 0
- If the peer needs its own compression, implement and negotiate a matching WebSocketExtension on both sides (offer it in the draft)
- Verify the client library version actually negotiated the extension it is using (check Sec-WebSocket-Extensions in the handshake)
- Handle close code 1002 and log the reported RSV bits to identify which bit the peer sets
Example fix
// before: client marks frames with RSV2 frame.setRSV(true, true, false); // after: only RSV1 for negotiated permessage-deflate frame.setRSV(compressed, false, false);
Defensive patterns
Strategy: try-catch
Validate before calling
// sender side: data frames may only set RSV1 when permessage-deflate is negotiated
if (isDataFrame && (rsv2 || rsv3)) throw new IllegalStateException("RSV2/RSV3 must be 0 on data frames"); Try / catch
@Override public void onClose(int code, String reason, boolean remote) {
if (code == 1002 && reason.startsWith("bad rsv")) { disableCompressionOnPeer(); reconnect(); }
} Prevention
- Use permessage-deflate via the library's PerMessageDeflateExtension instead of hand-rolled RSV flags
- Only set RSV1 for compression, and only when the extension was negotiated
- Log the 'bad rsv' message values to identify which bit the peer misuses
When it happens
Trigger: A peer sends a text/binary/continuation frame with RSV2=1 or RSV3=1 while permessage-deflate (or another RSV-claiming extension) is negotiated — the extension only owns RSV1. Typically a client implementing its own (non-negotiated) compression scheme.
Common situations: Client enabling a proprietary compression flag on frames without offering a matching extension in the handshake; buggy/fuzzing clients setting random RSV bits; a custom extension on one side not mirrored in the server's extension negotiation.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Continuous frame cannot have RSV1, RSV2 or RSV3 set
- 1007
- bad rsv RSV1: RSV2: RSV3
- 1008
- Inflated fragment size exceeds limit of
AI-assisted analysis of TooTallNate/Java-WebSocket@afeacbf8c0 (2026-09-09).
Data as JSON: /api/errors/04983bfa4aa0dc93.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/org/java_websocket/extensions/CompressionExtension.java:44
package org.java_websocket.extensions;
import org.java_websocket.exceptions.InvalidDataException;
import org.java_websocket.exceptions.InvalidFrameException;
import org.java_websocket.framing.ControlFrame;
import org.java_websocket.framing.DataFrame;
import org.java_websocket.framing.Framedata;
/**
* Implementation for a compression extension specified by https://tools.ietf.org/html/rfc7692
*
* @since 1.3.5
*/
public abstract class CompressionExtension extends DefaultExtension {
@Override
public void isFrameValid(Framedata inputFrame) throws InvalidDataException {
if ((inputFrame instanceof DataFrame) && (inputFrame.isRSV2() || inputFrame.isRSV3())) {
throw new InvalidFrameException(
"bad rsv RSV1: " + inputFrame.isRSV1() + " RSV2: " + inputFrame.isRSV2() + " RSV3: "
+ inputFrame.isRSV3());
}
if ((inputFrame instanceof ControlFrame) && (inputFrame.isRSV1() || inputFrame.isRSV2()
|| inputFrame.isRSV3())) {
throw new InvalidFrameException(
"bad rsv RSV1: " + inputFrame.isRSV1() + " RSV2: " + inputFrame.isRSV2() + " RSV3: "
+ inputFrame.isRSV3());
}
}
}
View on GitHub (pinned to afeacbf8c0)