TooTallNate/Java-WebSocket · error · InvalidFrameException

bad rsv RSV1: RSV2: RSV3

Error message

bad rsv RSV1: {rsv1} RSV2: {rsv2} RSV3: {rsv3}

What it means

DefaultExtension.isFrameValid is the base validation for extensions with no negotiated RSV usage: it rejects any frame with RSV1, RSV2, or RSV3 set, throwing InvalidFrameException (close code 1002, PROTOCOL_ERROR). This is the generic 'RSV bits set but no extension negotiated to use them' violation required by RFC 6455.

Solutions

  1. Enable the compression extension on the server: new Draft_6455(Collections.singletonList(new PerMessageDeflateExtension()))
  2. Ensure client and server drafts negotiate the same extensions (check Sec-WebSocket-Extensions in the handshake)
  3. If no compression is intended, fix the peer to send frames with all RSV bits cleared
  4. Note this exact call also appears in tests (testIsFrameValid); when writing extensions, override isFrameValid to allow the RSV bits your extension owns

Example fix

// before: server draft without extensions, client sends RSV1 frames
WebSocketServer wss = new WebSocketServer(addr, Collections.singletonList(new Draft_6455()));
// after
Draft_6455 draft = new Draft_6455(Collections.singletonList(new PerMessageDeflateExtension()));
WebSocketServer wss = new WebSocketServer(addr, Collections.singletonList(draft));
Defensive patterns

Strategy: try-catch

Validate before calling

// verify extension negotiation before relying on RSV1 compression
String ext = responseHeader("Sec-WebSocket-Extensions");
boolean deflateNegotiated = ext != null && ext.contains("permessage-deflate");
if (!deflateNegotiated) { compressFrames = false; }

Try / catch

@Override public void onClose(int code, String reason, boolean remote) {
  if (code == 1002 && reason.startsWith("bad rsv")) {
    // peer sent RSV bits but no extension was negotiated
    reconnectWithoutCompression();
  }
}

Prevention

When it happens

Trigger: A peer sets any RSV bit (e.g. RSV1=1 implying permessage-deflate) on a connection where only DefaultExtension is negotiated — i.e. the client offered the extension but the server declined it, or the client sets RSV bits without offering the extension at all.

Common situations: Client always marks frames compressed but the server has no compression extension in its draft (new Draft_6455() without extensions); mismatched drafts between client and server after a library upgrade; proxies stripping the Sec-WebSocket-Extensions header so negotiation silently fails.

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/4d6470a9dc0ed51a. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/org/java_websocket/extensions/DefaultExtension.java:64

  @Override
  public void encodeFrame(Framedata inputFrame) {
    //Nothing to do here
  }

  @Override
  public boolean acceptProvidedExtensionAsServer(String inputExtension) {
    return true;
  }

  @Override
  public boolean acceptProvidedExtensionAsClient(String inputExtension) {
    return true;
  }

  @Override
  public void isFrameValid(Framedata inputFrame) throws InvalidDataException {
    if (inputFrame.isRSV1() || inputFrame.isRSV2() || inputFrame.isRSV3()) {
      throw new InvalidFrameException(
          "bad rsv RSV1: " + inputFrame.isRSV1() + " RSV2: " + inputFrame.isRSV2() + " RSV3: "
              + inputFrame.isRSV3());
    }
  }

  @Override
  public String getProvidedExtensionAsClient() {
    return "";
  }

  @Override
  public String getProvidedExtensionAsServer() {
    return "";
  }

  @Override
  public IExtension copyInstance() {
    return new DefaultExtension();

View on GitHub (pinned to afeacbf8c0)