seanmonstar/warp · error

websocket handshake thread panicked

Error message

websocket handshake thread panicked

What it means

In warp's test utilities, WsClient handshake awaits the oneshot channel from the spawned handshake thread; if that thread panics (sender dropped), handshake panics with 'websocket handshake thread panicked'. This masks the underlying panic in the filter/service being tested.

Solutions

  1. Inspect test output for the ORIGINAL panic message in the spawned thread (often printed before this one)
  2. Fix the panic inside the ws filter/handler (avoid unwrap/assert on request data)
  3. Ensure the route under test actually applies filters::ws() and accepts the connection

Example fix

// before
let client = ws::handshake().await?; // panics when handler panics
// after
// make the handler fallible instead of panicking:
.and_then(|input| async move {
    parse(input).map_err(|e| warp::reject::custom(e))
})
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure the route under test contains the ws filter before handshaking
let route = warp::path("ws").and(warp::ws());

Try / catch

// the panic happens inside warp's spawned thread; use catch_unwind around the test body
let result = std::panic::catch_unwind(|| {
    let rt = tokio::runtime::Runtime::new().unwrap();
    rt.block_on(async { ws::ws_client().handshake().await })
});
assert!(result.is_ok(), "handshake thread panicked — check filter for panics");

Prevention

When it happens

Trigger: Calling .handshake() on a warp test WebSocket client when the ws filter rejects/panics during the upgrade — e.g. an assert! or unwrap inside the filter's closure fails before the upgrade completes.

Common situations: Integration tests where the handler panics on unexpected input, or the route doesn't actually include a ws() filter so the upgrade never happens.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of seanmonstar/warp@ff34d7213e (2026-09-09). Data as JSON: /api/errors/7c2ae6784395ec10. Report an issue: GitHub.

Appendix: source

Thrown at src/test.rs:580

                .take_while(|result| match result {
                    Err(_) => future::ready(false),
                    Ok(m) => future::ready(!m.is_close()),
                })
                .for_each(move |item| {
                    rd_tx.unbounded_send(item).expect("ws receive error");
                    future::ready(())
                });

            future::join(write, read).await;
        });

        match upgraded_rx.await {
            Ok(Ok(())) => Ok(WsClient {
                tx: wr_tx,
                rx: rd_rx,
            }),
            Ok(Err(err)) => Err(WsError::new(err)),
            Err(_canceled) => panic!("websocket handshake thread panicked"),
        }
    }
}

#[cfg(feature = "websocket")]
impl WsClient {
    /// Send a "text" websocket message to the server.
    pub async fn send_text(&mut self, text: impl Into<String>) {
        self.send(crate::ws::Message::text(text.into())).await;
    }

    /// Send a websocket message to the server.
    pub async fn send(&mut self, msg: crate::ws::Message) {
        self.tx.unbounded_send(msg).unwrap();
    }

    /// Receive a websocket message from the server.
    pub async fn recv(&mut self) -> Result<crate::filters::ws::Message, WsError> {

View on GitHub (pinned to ff34d7213e)