actix/actix-web · error · ProtocolError

invalid control frame length ({})

Error message

invalid control frame length ({})

What it means

ProtocolError::InvalidLength(usize) (ws/mod.rs:43) is raised in frame.rs:135-137 when a Ping or Pong control frame carries a payload longer than 125 bytes. RFC 6455 §5.5 mandates control frames have a payload length <= 125 and forbids them from being fragmented. actix closes the connection with a protocol error.

Source

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

};

/// 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,

View on GitHub (pinned to 937960ca67)

Solutions

  1. Keep Ping/Pong payloads at or below 125 bytes; move large data to Text/Binary frames.
  2. Update the offending peer's WebSocket library to an RFC-compliant version.
  3. If unavoidable, terminate the connection cleanly and reconnect.

Example fix

// before: oversize ping
ws.send(Message::Ping(vec![0u8; 200]))

// after: keep control payload <= 125 bytes
ws.send(Message::Ping(vec![0u8; 32]))
Defensive patterns

Strategy: validation

Validate before calling

// When sending Ping/Pong, enforce the 125-byte control-frame limit.
fn safe_ping(data: Vec<u8>) -> Result<Vec<u8>, &'static str> {
    if data.len() > 125 { Err("control frame payload must be <= 125 bytes") }
    else { Ok(data) }
}

Try / catch

match ws_stream.send(Message::Ping(payload)) {
    Err(ProtocolError::InvalidLength(n)) => {
        log::warn!("peer sent oversize control frame ({n} bytes); closing");
        ws_stream.close(Some(CloseReason::from(CloseCode::Protocol))).await.ok();
    }
    Err(e) => { /* handle other errors */ }
    Ok(_) => {}
}

Prevention

When it happens

Trigger: A WebSocket peer sends a Ping or Pong frame whose payload exceeds 125 bytes (e.g. embedding a large nonce or heartbeat data). For Close frames the same length is tolerated (frame.rs:138-141 morphs them), but Ping/Pong are rejected.

Common situations: Custom heartbeat implementations that overload Ping with oversized application data, or a client library that ignores the 125-byte control-frame limit. Rare with mainstream browsers.

Related errors


AI-assisted analysis of actix/actix-web@937960ca67 (2026-08-06). Data as JSON: /data/errors/ef57958d653368a7.json. Report an issue: GitHub.