actix/actix-web · error · ProtocolError

invalid control frame length

Error message

invalid control frame length ({})

What it means

ProtocolError::InvalidLength(usize) is raised in frame.rs:136 when a Ping or Pong control frame carries a payload longer than 125 bytes. RFC 6455 §5.5 mandates that all control frames have a payload length <= 125 and must set the FIN bit, so a larger Ping/Pong is a protocol violation. The length is read from the frame header in parse_metadata and checked after the payload is fully received.

Solutions

  1. Keep Ping/Pong payloads at or below 125 bytes; use Text/Binary frames for larger data.
  2. If you are the sender, use the standard Message::Ping which the Codec frames correctly.
  3. Close the connection on this error (the actix WS dispatcher does so automatically).

Example fix

// before: oversized ping payload
let big = vec![0u8; 200];
msg = Message::Ping(Bytes::from(big));

// after: payload <= 125 bytes
let small = vec![0u8; 64];
msg = Message::Ping(Bytes::from(small));
Defensive patterns

Strategy: validation

Validate before calling

// Cap ping/pong payloads before sending
fn safe_ping(data: Vec<u8>) -> Message {
    let d = if data.len() > 125 { data[..125].to_vec() } else { data };
    Message::Ping(Bytes::from(d))
}

Try / catch

match parse_result {
    Err(ProtocolError::InvalidLength(n)) => {
        log::warn!("control frame too long: {n}");
        close_with(CloseCode::Protocol);
    }
    _ => { /* ... */ }
}

Prevention

When it happens

Trigger: A peer sends a Ping or Pong frame whose declared payload length exceeds 125 bytes (e.g. a 200-byte ping). Close frames over 125 are not errored - they are morphed to an empty close (frame.rs:138) - but Ping/Pong are.

Common situations: A client library that incorrectly fragments or oversizes keep-alive Ping payloads; stress tests sending large pings; a faulty intermediary that re-encodes control frames.

Related errors


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

Appendix: source

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

/// WebSocket protocol errors.
#[derive(Debug, Display, Error, From)]
pub enum ProtocolError {
    /// Received an unmasked frame from client.
    #[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),

    /// Invalid control frame length
    #[display("invalid control frame length ({})", _0)]
    InvalidLength(#[error(not(source))] usize),

    /// Bad opcode.
    #[display("bad opcode")]
    BadOpCode,

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

    /// Continuation has not started.
    #[display("continuation has not started")]
    ContinuationNotStarted,

    /// Received new continuation but it is already started.
    #[display("received new continuation but it has already started")]
    ContinuationStarted,

    /// Unknown continuation fragment.

View on GitHub (pinned to 4d435abc28)