actix/actix-web · error · ProtocolError

Invalid control frame length ({})

Error message

Invalid control frame length ({})

What it means

This is `ws::ProtocolError::InvalidLength(usize)` from actix-http's WebSocket module. RFC 6455 forbids control frames (ping, pong, close) from having a payload longer than 125 bytes, and actix-http's parser raises this error when a control frame exceeds that limit. It indicates the remote peer produced a spec-violating frame.

Source

Thrown at actix-http/src/ws/mod.rs:48

    #[display("Received an unmasked frame from client")]
    UnmaskedFrame,

    /// Received a masked frame from server.
    #[display("Received a masked frame from server")]
    MaskedFrame,

    /// Encountered invalid opcode.
    #[display("Invalid opcode ({})", _0)]
    InvalidOpcode(#[error(not(source))] u8),

    // TODO(semver-major):
    // /// Received a frame with non-zero reserved bits.
    // #[display("Received a frame with non-zero reserved bits")]
    // InvalidReservedBits,
    //
    /// Invalid control frame length
    #[display("Invalid control frame length ({})", _0)]
    InvalidLength(#[error(not(source))] usize),

    // TODO(semver-major): use in Parser::try_parse_close_payload
    //
    // /// Invalid close status code.
    // #[display("Invalid close status code ({})", _0)]
    // InvalidCloseCode(#[error(not(source))] u16),
    //
    // /// Invalid UTF-8 close reason.
    // #[display("Invalid UTF-8 close reason")]
    // InvalidCloseReason,
    //
    /// Bad opcode.
    #[display("Bad opcode")]
    BadOpCode,

    /// A payload reached size limit.
    #[display("Payload reached size limit")]
    Overflow,

View on GitHub (pinned to c215607f4b)

Solutions

  1. Fix the peer implementation to keep control frame payloads at 125 bytes or fewer (truncate close reasons/ping payloads).
  2. On the actix side, keep `ws::Client`/ping payloads small; do not attach large data to ping frames.
  3. Treat the error as a fatal protocol violation: close the connection with CloseCode::Protocol rather than attempting to continue.
  4. If you control both ends, capture the reported length value to confirm the size rule that is being violated before patching the peer.

Example fix

// before
let payload = reason.as_bytes(); // could exceed 125 bytes
ws.close(Some(CloseReason { code: CloseCode::Normal, description: Some(reason) }));
// after
let description = if reason.len() > 123 { &reason[..123] } else { &reason[..] };
ws.close(Some(CloseReason { code: CloseCode::Normal, description: Some(description.to_string()) }));
Defensive patterns

Strategy: validation

Validate before calling

fn control_frame_payload_ok(opcode: u8, len: usize) -> bool {
    !matches!(opcode, 0x8 | 0x9 | 0xA) || len <= 125
}

Type guard

fn is_control_frame(opcode: u8) -> bool {
    matches!(opcode, 0x8 | 0x9 | 0xA)
}

Try / catch

match result {
    Err(ProtocolError::InvalidLength(n)) => {
        log::warn!("control frame payload of {n} bytes exceeds 125; closing");
        close_with(CloseCode::Protocol);
    }
    other => handle(other),
}

Prevention

When it happens

Trigger: Parser::parse encounters a control frame (opcode 0x8/0x9/0xA) whose payload length field is > 125 bytes.

Common situations: Interoperating with a custom WebSocket client that fragments or oversizes pings/pongs, a buggy library sending large close reasons without truncation, or a test harness generating oversized control frames.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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