{"record":{"id":"58baf2ee482dbb82","repo":"actix/actix-web","slug":"invalid-opcode-58baf2","errorCode":null,"errorMessage":"Invalid opcode ({})","messagePattern":"Invalid opcode \\((.+?)\\)","errorType":"validation","errorClass":"ProtocolError","httpStatus":null,"severity":"error","filePath":"actix-http/src/ws/mod.rs","lineNumber":39,"sourceCode":"    dispatcher::Dispatcher,\n    frame::Parser,\n    proto::{hash_key, CloseCode, CloseReason, OpCode},\n};\n\n/// WebSocket protocol errors.\n#[derive(Debug, Display, Error, From)]\npub enum ProtocolError {\n    /// Received an unmasked frame from client.\n    #[display(\"Received an unmasked frame from client\")]\n    UnmaskedFrame,\n\n    /// Received a masked frame from server.\n    #[display(\"Received a masked frame from server\")]\n    MaskedFrame,\n\n    /// Encountered invalid opcode.\n    #[display(\"Invalid opcode ({})\", _0)]\n    InvalidOpcode(#[error(not(source))] u8),\n\n    // TODO(semver-major):\n    // /// Received a frame with non-zero reserved bits.\n    // #[display(\"Received a frame with non-zero reserved bits\")]\n    // InvalidReservedBits,\n    //\n    /// Invalid control frame length\n    #[display(\"Invalid control frame length ({})\", _0)]\n    InvalidLength(#[error(not(source))] usize),\n\n    // TODO(semver-major): use in Parser::try_parse_close_payload\n    //\n    // /// Invalid close status code.\n    // #[display(\"Invalid close status code ({})\", _0)]\n    // InvalidCloseCode(#[error(not(source))] u16),\n    //\n    // /// Invalid UTF-8 close reason.\n    // #[display(\"Invalid UTF-8 close reason\")]","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/actix/actix-web/blob/c215607f4b6e9ba9accff4a949a33c98f218db20/actix-http/src/ws/mod.rs#L21-L57","documentation":"This error is `ws::ProtocolError::InvalidOpcode(u8)` from actix-http's WebSocket implementation. It means a WebSocket frame arrived whose opcode byte (the low 4 bits of the first frame byte) is not one of the opcodes defined by RFC 6455 (continuation 0x0, text 0x1, binary 0x2, close 0x8, ping 0x9, pong 0xA). actix-http's frame parser rejects such frames because it cannot interpret the payload correctly, so the connection is failed with a protocol error.","triggerScenarios":"The peer sends a WebSocket frame whose 4-bit opcode is 0x3-0x7 (reserved non-control) or 0xB-0xF (reserved control), e.g. during Parser::parse of an incoming frame on a ws session.","commonSituations":"Talking to a non-conformant or hand-rolled WebSocket client/server, a broken intermediary/proxy corrupting frame headers, desynchronized framing after a payload-length parsing bug, or an implementation using private/reserved extensions without a negotiated extension.","solutions":["Fix or replace the non-conformant peer/intermediary so it emits only RFC 6455 opcodes (0x0,0x1,0x2,0x8,0x9,0xA).","Ensure any WebSocket extensions (e.g. permessage-deflate) are actually negotiated in the handshake before frames with extension-specific framing are sent.","Verify TLS/transport integrity; a corrupting proxy or mis-framed stream can shift opcode bytes, so reset the connection and re-handshake.","Log the offending opcode value from the error payload and compare against RFC 6455 Section 5.2 to identify which reserved opcode the peer emits."],"exampleFix":"// before: treating every ProtocolError as a retryable bug\nErr(ProtocolError::InvalidOpcode(op)) => panic!(\"bad opcode {}\", op),\n// after: it is a peer protocol violation; close the connection cleanly\nErr(err @ ProtocolError::InvalidOpcode(_)) => {\n    log::warn!(\"peer sent reserved opcode; closing: {err}\");\n    ws.close(Some(CloseReason::from(CloseCode::Protocol)))\n}","handlingStrategy":"try-catch","validationCode":"const VALID_OPCODES = new Set([0x0, 0x1, 0x2, 0x8, 0x9, 0xA]);\n// before sending/forwarding a frame:\nif (!VALID_OPCODES.has(frame.opcode)) throw new Error(`peer would send reserved opcode ${frame.opcode}`);","typeGuard":"fn is_valid_opcode(op: u8) -> bool {\n    matches!(op, 0x0 | 0x1 | 0x2 | 0x8 | 0x9 | 0xA)\n}","tryCatchPattern":"match result {\n    Err(ProtocolError::InvalidOpcode(op)) => {\n        log::warn!(\"reserved opcode {} from peer; closing\", op);\n        close_with(CloseCode::Protocol);\n    }\n    Err(e) => handle_protocol_error(e),\n    Ok(msg) => process(msg),\n}","preventionTips":["Only interoperate with RFC 6455-compliant WebSocket implementations","Never use reserved opcodes for private signaling; use ping payloads or an application-level field","Validate any custom proxy/intermediary preserves frame bytes exactly","Log the opcode value on failure to identify the offending peer quickly"],"tags":["websocket","protocol","rfc6455","actix-http"],"backgroundTag":"invalid-enum-value","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"}