PR #648 fixed the esp_websocket_client_close unwrap in Drop, but esp_websocket_client_destroy still calls .unwrap():
impl Drop for EspWebSocketClient<'_> {
fn drop(&mut self) {
if let Err(e) = esp!(unsafe { esp_websocket_client_close(self.handle, self.timeout) }) {
log::warn!("WebSocket close failed during drop: {e:?}");
}
esp!(unsafe { esp_websocket_client_destroy(self.handle) }).unwrap(); // can still panic
}
}
Why this is a problem
- Drop must not panic. On ESP-IDF targets using panic = "abort", any panic in Drop aborts the entire application — there is no unwinding to catch it. A
Drop impl should be infallible.
- The new_raw construction order triggers this. The client struct is constructed before esp_websocket_client_start() is called. When start() fails (e.g.,
server unreachable), the ? operator returns Err, dropping the already-constructed client. Drop then runs on a client whose transport was never established.
- esp_websocket_client_destroy can return errors. While it typically returns ESP_OK, the ESP-IDF documentation does not guarantee this. Future ESP-IDF
versions or edge cases (e.g., double-free, corrupted handle after a failed start) could cause it to fail.
Suggested fix
Apply the same pattern as close — or simpler, just ignore errors from both cleanup calls:
impl Drop for EspWebSocketClient<'_> {
fn drop(&mut self) {
let _ = esp!(unsafe { esp_websocket_client_close(self.handle, self.timeout) });
let _ = esp!(unsafe { esp_websocket_client_destroy(self.handle) });
}
}
Reproduction
- Configure EspWebSocketClient with disable_auto_reconnect: true pointing at a non-existent server
- Call EspWebSocketClient::new() — the TCP connection fails asynchronously
- The client is dropped (either from new_raw error path or after detecting the failure)
- Result: abort() was called — application crashes
Environment
- ESP32-P4, esp-idf-svc 0.52.1, ESP-IDF v5.4.3 / v5.5.4
- panic = "abort" (default for ESP-IDF targets)
PR #648 fixed the esp_websocket_client_close unwrap in Drop, but esp_websocket_client_destroy still calls .unwrap():
Why this is a problem
Drop impl should be infallible.
server unreachable), the ? operator returns Err, dropping the already-constructed client. Drop then runs on a client whose transport was never established.
versions or edge cases (e.g., double-free, corrupted handle after a failed start) could cause it to fail.
Suggested fix
Apply the same pattern as close — or simpler, just ignore errors from both cleanup calls:
Reproduction
Environment