{"record":{"id":"11f34e7d262ee8ac","repo":"actix/actix-web","slug":"received-a-frame-with-non-zero-reserved-bits","errorCode":null,"errorMessage":"Received a frame with non-zero reserved bits","messagePattern":"Received a frame with non-zero reserved bits","errorType":"exception","errorClass":"ProtocolError","httpStatus":null,"severity":"error","filePath":"actix-http/src/ws/frame.rs","lineNumber":35,"sourceCode":"    fn parse_metadata(\n        src: &[u8],\n        server: bool,\n    ) -> Result<Option<(usize, bool, OpCode, usize, Option<[u8; 4]>)>, ProtocolError> {\n        let chunk_len = src.len();\n\n        let mut idx = 2;\n        if chunk_len < 2 {\n            return Ok(None);\n        }\n\n        let first = src[0];\n        let second = src[1];\n        let finished = first & 0x80 != 0;\n\n        // RSV1, RSV2, and RSV3 must be zero unless a negotiated extension defines them.\n        if first & 0b0111_0000 != 0 {\n            // TODO(semver-major): use InvalidReservedBits\n            return Err(ProtocolError::Io(io::Error::new(\n                io::ErrorKind::InvalidData,\n                \"Received a frame with non-zero reserved bits\",\n            )));\n        }\n\n        // check masking\n        let masked = second & 0x80 != 0;\n        if !masked && server {\n            return Err(ProtocolError::UnmaskedFrame);\n        } else if masked && !server {\n            return Err(ProtocolError::MaskedFrame);\n        }\n\n        // Op code\n        let opcode = OpCode::from(first & 0x0F);\n\n        if let OpCode::Bad = opcode {\n            return Err(ProtocolError::InvalidOpcode(first & 0x0F));","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/actix/actix-web/blob/c215607f4b6e9ba9accff4a949a33c98f218db20/actix-http/src/ws/frame.rs#L17-L53","documentation":"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.)","triggerScenarios":"`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.","commonSituations":"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.","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."],"exampleFix":"// before: client compresses unilaterally\nws = new WebSocket(url); // sends RSV1 frames via its own deflate\n// after: negotiate the extension with the server first\nws = new WebSocket(url, { perMessageDeflate: true }); // server must accept Sec-WebSocket-Extensions: permessage-deflate","handlingStrategy":"try-catch","validationCode":"// peer-side check before sending: RSV bits must be 0 unless an extension was negotiated\nif ((firstByte & 0b0111_0000) !== 0 && !extensionsNegotiated) {\n    throw new Error(\"cannot set RSV bits: no WebSocket extension negotiated\");\n}","typeGuard":"fn rsv_bits_set(first_byte: u8) -> bool {\n    first_byte & 0b0111_0000 != 0\n}","tryCatchPattern":"match result {\n    Err(ProtocolError::Io(ref e)) if e.to_string().contains(\"non-zero reserved bits\") => {\n        log::warn!(\"peer sent RSV!=0 without negotiated extension; disabling compression assumptions and closing\");\n        close_with(CloseCode::Protocol);\n    }\n    other => handle(other),\n}","preventionTips":["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"],"tags":["websocket","protocol","rfc6455","reserved-bits","compression"],"backgroundTag":"unsupported-operation","analyzedSha":"c215607f4b6e9ba9accff4a949a33c98f218db20","analyzedAt":"2026-09-09T15:57:34.010Z","contentChangedAt":"2026-09-09T15:57:34.010Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}