Skip to content

EspWebSocketClient::Drop still panics: esp_websocket_client_destroy unwrap #653

@fuk-yu

Description

@fuk-yu

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

  1. 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.
  2. 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.
  3. 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

  1. Configure EspWebSocketClient with disable_auto_reconnect: true pointing at a non-existent server
  2. Call EspWebSocketClient::new() — the TCP connection fails asynchronously
  3. The client is dropped (either from new_raw error path or after detecting the failure)
  4. 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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Review

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions