actix/actix-web · error · ProtocolError

Received a frame with non-zero reserved bits

Error message

Received a frame with non-zero reserved bits

What it means

In actix-http's WebSocket frame parser (`Parser::parse_metadata`), the three RSV bits (RSV1-3) of the first frame byte must be zero unless a WebSocket extension has been negotiated. When they are non-zero and no extension defines them, the parser returns `ProtocolError::Io` wrapping an InvalidData io::Error with this message. (Note the TODO: a dedicated `InvalidReservedBits` variant is planned for a semver-major release.)

Source

Thrown at actix-http/src/ws/frame.rs:35

    fn parse_metadata(
        src: &[u8],
        server: bool,
    ) -> Result<Option<(usize, bool, OpCode, usize, Option<[u8; 4]>)>, ProtocolError> {
        let chunk_len = src.len();

        let mut idx = 2;
        if chunk_len < 2 {
            return Ok(None);
        }

        let first = src[0];
        let second = src[1];
        let finished = first & 0x80 != 0;

        // RSV1, RSV2, and RSV3 must be zero unless a negotiated extension defines them.
        if first & 0b0111_0000 != 0 {
            // TODO(semver-major): use InvalidReservedBits
            return Err(ProtocolError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Received a frame with non-zero reserved bits",
            )));
        }

        // check masking
        let masked = second & 0x80 != 0;
        if !masked && server {
            return Err(ProtocolError::UnmaskedFrame);
        } else if masked && !server {
            return Err(ProtocolError::MaskedFrame);
        }

        // Op code
        let opcode = OpCode::from(first & 0x0F);

        if let OpCode::Bad = opcode {
            return Err(ProtocolError::InvalidOpcode(first & 0x0F));

View on GitHub (pinned to c215607f4b)

Solutions

  1. Enable the extension the peer expects — for compression, install the permessage-deflate support in your actix ws handshake so RSV1 frames are legal.
  2. Fix the peer/client not to set RSV bits when no extension was negotiated in the Sec-WebSocket-Extensions handshake response.
  3. Disable compression on the client side so it emits plain RFC 6455 frames with RSV=0.
  4. Check intermediaries (proxies, CDN WebSocket passthrough) for frame corruption and bypass them to test.

Example fix

// before: client compresses unilaterally
ws = new WebSocket(url); // sends RSV1 frames via its own deflate
// after: negotiate the extension with the server first
ws = new WebSocket(url, { perMessageDeflate: true }); // server must accept Sec-WebSocket-Extensions: permessage-deflate
Defensive patterns

Strategy: try-catch

Validate before calling

// peer-side check before sending: RSV bits must be 0 unless an extension was negotiated
if ((firstByte & 0b0111_0000) !== 0 && !extensionsNegotiated) {
    throw new Error("cannot set RSV bits: no WebSocket extension negotiated");
}

Type guard

fn rsv_bits_set(first_byte: u8) -> bool {
    first_byte & 0b0111_0000 != 0
}

Try / catch

match result {
    Err(ProtocolError::Io(ref e)) if e.to_string().contains("non-zero reserved bits") => {
        log::warn!("peer sent RSV!=0 without negotiated extension; disabling compression assumptions and closing");
        close_with(CloseCode::Protocol);
    }
    other => handle(other),
}

Prevention

When it happens

Trigger: `Parser::parse_metadata` reads a first frame byte where `first & 0b0111_0000 != 0`, i.e. any RSV bit is set while no negotiated extension (e.g. permessage-deflate) is active for the connection.

Common situations: A client or intermediary using permessage-deflate compression while the server never accepted the extension in the handshake, a custom client flipping RSV bits for proprietary signaling, or corrupted frame bytes from a misbehaving proxy.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of actix/actix-web@c215607f4b (2026-09-09). Data as JSON: /api/errors/11f34e7d262ee8ac. Report an issue: GitHub.