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
- Enable the extension the peer expects — for compression, install the permessage-deflate support in your actix ws handshake so RSV1 frames are legal.
- Fix the peer/client not to set RSV bits when no extension was negotiated in the Sec-WebSocket-Extensions handshake response.
- Disable compression on the client side so it emits plain RFC 6455 frames with RSV=0.
- 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
- Only set RSV bits if the handshake's Sec-WebSocket-Extensions response accepted the extension
- Match compression settings on both ends: enable permessage-deflate on the server if clients send RSV1-compressed frames
- Disable permessage-deflate on clients when connecting to servers without extension support
- Audit proxies/CDNs in the path for WebSocket frame mutation
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
- Invalid opcode ({})
- Invalid control frame length ({})
- invalid opcode ({})
- invalid control frame length ({})
- unknown continuation fragment: {}
AI-assisted analysis of actix/actix-web@c215607f4b (2026-09-09).
Data as JSON: /api/errors/11f34e7d262ee8ac.
Report an issue: GitHub.