{"record":{"id":"7c2ae6784395ec10","repo":"seanmonstar/warp","slug":"websocket-handshake-thread-panicked","errorCode":null,"errorMessage":"websocket handshake thread panicked","messagePattern":"websocket handshake thread panicked","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/test.rs","lineNumber":580,"sourceCode":"                .take_while(|result| match result {\n                    Err(_) => future::ready(false),\n                    Ok(m) => future::ready(!m.is_close()),\n                })\n                .for_each(move |item| {\n                    rd_tx.unbounded_send(item).expect(\"ws receive error\");\n                    future::ready(())\n                });\n\n            future::join(write, read).await;\n        });\n\n        match upgraded_rx.await {\n            Ok(Ok(())) => Ok(WsClient {\n                tx: wr_tx,\n                rx: rd_rx,\n            }),\n            Ok(Err(err)) => Err(WsError::new(err)),\n            Err(_canceled) => panic!(\"websocket handshake thread panicked\"),\n        }\n    }\n}\n\n#[cfg(feature = \"websocket\")]\nimpl WsClient {\n    /// Send a \"text\" websocket message to the server.\n    pub async fn send_text(&mut self, text: impl Into<String>) {\n        self.send(crate::ws::Message::text(text.into())).await;\n    }\n\n    /// Send a websocket message to the server.\n    pub async fn send(&mut self, msg: crate::ws::Message) {\n        self.tx.unbounded_send(msg).unwrap();\n    }\n\n    /// Receive a websocket message from the server.\n    pub async fn recv(&mut self) -> Result<crate::filters::ws::Message, WsError> {","sourceCodeStart":562,"sourceCodeEnd":598,"githubUrl":"https://github.com/seanmonstar/warp/blob/ff34d7213ed55ec342304aa7ff6ac4b351da9e66/src/test.rs#L562-L598","documentation":"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.","triggerScenarios":"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.","commonSituations":"Integration tests where the handler panics on unexpected input, or the route doesn't actually include a ws() filter so the upgrade never happens.","solutions":["Inspect test output for the ORIGINAL panic message in the spawned thread (often printed before this one)","Fix the panic inside the ws filter/handler (avoid unwrap/assert on request data)","Ensure the route under test actually applies filters::ws() and accepts the connection"],"exampleFix":"// before\nlet client = ws::handshake().await?; // panics when handler panics\n// after\n// make the handler fallible instead of panicking:\n.and_then(|input| async move {\n    parse(input).map_err(|e| warp::reject::custom(e))\n})","handlingStrategy":"try-catch","validationCode":"// ensure the route under test contains the ws filter before handshaking\nlet route = warp::path(\"ws\").and(warp::ws());","typeGuard":null,"tryCatchPattern":"// the panic happens inside warp's spawned thread; use catch_unwind around the test body\nlet result = std::panic::catch_unwind(|| {\n    let rt = tokio::runtime::Runtime::new().unwrap();\n    rt.block_on(async { ws::ws_client().handshake().await })\n});\nassert!(result.is_ok(), \"handshake thread panicked — check filter for panics\");","preventionTips":["Avoid unwrap/assert/panic inside ws filter handlers — return rejects instead","Verify the route includes filters::ws() before testing","Read the earlier panic message from the spawned thread to find the root cause"],"tags":["websocket","testing","panic","handshake"],"backgroundTag":"internal-invariant-violation","analyzedSha":"ff34d7213ed55ec342304aa7ff6ac4b351da9e66","analyzedAt":"2026-09-09T16:57:46.316Z","contentChangedAt":"2026-09-09T16:57:46.316Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}