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

  1. Fix the peer to only set RSV1 for compressed frames under permessage-deflate and leave RSV2/RSV3 = 0
  2. If the peer needs its own compression, implement and negotiate a matching WebSocketExtension on both sides (offer it in the draft)
  3. Verify the client library version actually negotiated the extension it is using (check Sec-WebSocket-Extensions in the handshake)
  4. 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

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


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)