actix/actix-web · error · ProtocolError

unknown continuation fragment

Error message

unknown continuation fragment: {}

What it means

ProtocolError::ContinuationFragment(OpCode) is raised in codec.rs:259 when an unfinished (FIN bit = 0) frame arrives with an opcode other than Continue/Text/Binary. RFC 6455 forbids fragmenting control frames (Close/Ping/Pong), so a non-final Close/Ping/Pong is logged as an unknown continuation fragment and rejected. It is distinct from ContinuationNotStarted/ContinuationStarted which track the fragmentation state machine.

Solutions

  1. Ensure control frames (Close/Ping/Pong) are always sent with FIN=1 and unfragmented.
  2. Use the actix Codec/Message API which never fragments control frames.
  3. Treat the error as fatal and close the connection (handled by the dispatcher).

Example fix

// before: control frame with FIN=0 (fragmented) - invalid
// after: always set FIN=1 on Ping/Pong/Close via the standard message API
frame.send(Message::Ping(payload)); // Codec sets FIN correctly
Defensive patterns

Strategy: try-catch

Try / catch

match decode_result {
    Err(actix_http::ws::ProtocolError::ContinuationFragment(op)) => {
        log::error!("fragmented control frame {op}");
        // RFC violation; terminate the connection
        return Err(CloseCode::Protocol.into());
    }
    _ => { /* ... */ }
}

Prevention

When it happens

Trigger: A peer sends a Close, Ping, or Pong frame with the FIN bit cleared (an attempt to fragment a control frame). The codec's decode (codec.rs:257) hits the wildcard arm and returns this error.

Common situations: A non-conformant client that fragments ping/pong keep-alives; a custom WS implementation misunderstanding the fragmentation rules; stream corruption flipping the FIN bit.

Related errors


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

Appendix: source

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

    /// 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.
    #[display("unknown continuation fragment: {}", _0)]
    ContinuationFragment(#[error(not(source))] OpCode),

    /// I/O error.
    #[display("I/O error: {}", _0)]
    Io(io::Error),
}

/// WebSocket handshake errors
#[derive(Debug, Clone, Copy, PartialEq, Eq, Display, Error)]
pub enum HandshakeError {
    /// Only get method is allowed.
    #[display("method not allowed")]
    GetMethodRequired,

    /// Upgrade header if not set to WebSocket.
    #[display("WebSocket upgrade is expected")]
    NoWebsocketUpgrade,

    /// Connection header is not set to upgrade.

View on GitHub (pinned to 4d435abc28)