TooTallNate/Java-WebSocket · error · InvalidDataException

1008

1008

Error message

{message}

What it means

During decompression of a permessage-deflate message, the inflater throws DataFormatException because the payload is not valid DEFLATE data. The extension wraps it in an InvalidDataException with close code 1008 (POLICY_VALIDATION), carrying the inflater's message (e.g. 'invalid distance too far back'), and the connection is closed.

Solutions

  1. Verify both endpoints correctly implement RFC 7692, including appending the empty deflate block (0x00 0x00 0xFF 0xFF) to the last fragment before inflating.
  2. Confirm the peer actually sends compressed payloads only when the permessage-deflate extension was negotiated; check the handshake's Sec-WebSocket-Extensions header.
  3. Check for proxies/middleware that alter or truncate frame payloads.
  4. Log the inflater's detail message (it's carried in the InvalidDataException) to pinpoint the deflate defect.

Example fix

// before: client sends raw uncompressed text after negotiating permessage-deflate
ws.send("plain text"); // peer's inflater fails

// after: use the library's send API so frames are compressed as negotiated
webSocket.send("plain text"); // library applies permessage-deflate correctly
Defensive patterns

Strategy: try-catch

Validate before calling

// sanity-check the negotiated extensions before assuming compressed frames
String ext = request.getHeader("Sec-WebSocket-Extensions");
boolean deflateNegotiated = ext != null && ext.contains("permessage-deflate");

Try / catch

try {
    byte[] data = decompressed(frame);
} catch (InvalidDataException e) {
    // close code 1008, invalid deflate payload
    logger.warn("deflate decode failed: {}", e.getMessage());
    webSocket.close(1008, "invalid compressed payload");
}

Prevention

When it happens

Trigger: decompress() is called (via decompressed()) and Inflater.inflate() throws DataFormatException — i.e. a frame payload claimed to be compressed (RSV1 set / negotiated permessage-deflate) is not valid zlib-deflate data or is missing the trailing 0x00 0x00 0xFF 0xFF empty block on the final fragment.

Common situations: Peers that claim permessage-deflate support but don't actually compress payloads; corruption by intermediaries/proxies; clients that forget the 4-byte tail block; double-decompression in custom code layered on the library.

Understand the failure class

Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.

Related errors


AI-assisted analysis of TooTallNate/Java-WebSocket@afeacbf8c0 (2026-09-09). Data as JSON: /api/errors/e9b81eb177f835b4. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/org/java_websocket/extensions/permessage_deflate/PerMessageDeflateExtension.java:252

      // RFC 7692: If the "agreed parameters" contain the "client|server_no_context_takeover"
      // extension parameter, the server|client MAY decompress each new message with an empty
      // LZ77 sliding window.
      if (isDecompressorResetAllowed) {
        decompressor.reset();
      }
    }
  }

  private byte[] decompress(ByteBuffer buffer, boolean isFinal) throws InvalidDataException {
    ByteArrayOutputStream decompressed = new ByteArrayOutputStream();
    try {
      decompress(buffer, decompressed);
      // RFC 7692: Append empty deflate block to the tail end of the payload of the message
      if (isFinal) {
        decompress(ByteBuffer.wrap(EMPTY_DEFLATE_BLOCK), decompressed);
      }
    } catch (DataFormatException e) {
      throw new InvalidDataException(CloseFrame.POLICY_VALIDATION, e.getMessage());
    }
    return decompressed.toByteArray();
  }

  private void decompress(ByteBuffer buffer, ByteArrayOutputStream decompressed)
      throws DataFormatException {
    if (buffer.hasArray()) {
      decompressor.setInput(
          buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
    } else {
      byte[] input = new byte[buffer.remaining()];
      buffer.duplicate().get(input);
      decompressor.setInput(input);
    }
    byte[] chunk = new byte[TRANSFER_CHUNK_SIZE];
    while (!decompressor.finished()) {
      int length = decompressor.inflate(chunk);
      if (length > 0) {

View on GitHub (pinned to afeacbf8c0)