actix/actix-web · error · ProtocolError

Unknown continuation fragment: {}

Error message

Unknown continuation fragment: {}

What it means

This is `ws::ProtocolError::ContinuationFragment(OpCode)` from actix-http's WebSocket handling. A fragmented message must consist of a first frame (text/binary) followed only by continuation frames (opcode 0x0). This error is raised when a continuation frame arrives when no fragmented message is in progress, i.e. the fragment sequence state machine is out of sync.

Source

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

    /// 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 c215607f4b)

Solutions

  1. Fix the sending side so every fragmented message starts with an initial text/binary frame (FIN=0) before any continuation frames.
  2. Ensure only the last fragment has FIN=1 and that no extra continuation frames are sent after it.
  3. Check for intermediaries (proxies/load balancers) that could reorder or duplicate WebSocket frames and bypass or replace them.
  4. On error, reset the per-connection message state and close with CloseCode::Protocol; do not attempt to salvage the partial message.

Example fix

// before: sender starts fragments with opcode 0x0
send_frame(Frame::continuation(payload).fin(false));
// after: first fragment must carry the message type
send_frame(Frame::text(payload).fin(false));
send_frame(Frame::continuation(rest).fin(true));
Defensive patterns

Strategy: try-catch

Validate before calling

// sender-side check before emitting a continuation frame
if (!fragmentationActive) throw new Error("continuation frame without an open fragmented message");

Type guard

struct FragmentState { in_progress: bool }
fn can_send_continuation(state: &FragmentState) -> bool {
    state.in_progress
}

Try / catch

match result {
    Err(ProtocolError::ContinuationFragment(op)) => {
        log::warn!("unexpected continuation fragment (op {:?}); resetting message state", op);
        reset_assembler();
        close_with(CloseCode::Protocol);
    }
    other => handle(other),
}

Prevention

When it happens

Trigger: The ws message assembler receives a continuation frame (opcode 0x0) while `continuation` state is None/finished — e.g. a lone continuation frame or a continuation after the FIN frame already closed the message.

Common situations: A peer that emits continuation frames without a preceding fragmented text/binary frame, double-sending the final fragment, a proxy reordering/dropping frames, or resuming a session mid-message after reconnecting.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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