Where
crates/buttplug_server_hwmgr_webbluetooth/src/webbluetooth_hardware.rs, the WebBluetoothDeviceCommand::Read handler:
let data_view = val; // resolved from characteristic.readValue() => a DataView
let mut body = vec![0u8; data_view.byte_length()];
Uint8Array::new(&data_view).copy_to(&mut body[..]);
What happens
readValue() resolves to a DataView. In JS, new Uint8Array(dataView) does not view the DataView's buffer — a DataView is treated as a plain array-like with length === undefined, so the result is an empty Uint8Array. copy_to then hits js-sys's length assertion and panics, which kills the whole wasm instance:
panicked at js-sys-0.3.104/src/lib.rs:14143:1:
assertion `left == right` failed
left: 0
right: 15
Uncaught RuntimeError: unreachable
Net effect: any device whose protocol performs a hardware read over WebBluetooth dies silently after the chooser pairing — the device never appears, and the embedded server is dead from that point on. Native btleplug paths are unaffected, so the same device works fine through Intiface Central, which makes this confusing to diagnose from the outside.
Repro
Sensee Capsule (CCPA10S2, protocol sensee-v2 — its initializer reads model data from Tx). Chrome/macOS, chooser pairs fine, then the panic above. Any read-during-init protocol should reproduce.
Fix
Build the view over the underlying buffer, same as the Subscribe handler a few lines below already does:
let data_view = js_sys::DataView::from(val);
let mut body = vec![0u8; data_view.byte_length()];
Uint8Array::new_with_byte_offset_and_length(
&JsValue::from(data_view.buffer()),
data_view.byte_offset() as u32,
data_view.byte_length() as u32,
)
.copy_to(&mut body[..]);
Patched in our local wasm build and verified against a physical Sensee Capsule (CCPA10S2): device now identifies and runs (vibrate + constrict) through the browser embedded server. Happy to open a PR with this if you'd like (opening the issue first per the contribution policy).
Where
crates/buttplug_server_hwmgr_webbluetooth/src/webbluetooth_hardware.rs, theWebBluetoothDeviceCommand::Readhandler:What happens
readValue()resolves to a DataView. In JS,new Uint8Array(dataView)does not view the DataView's buffer — a DataView is treated as a plain array-like withlength === undefined, so the result is an empty Uint8Array.copy_tothen hits js-sys's length assertion and panics, which kills the whole wasm instance:Net effect: any device whose protocol performs a hardware read over WebBluetooth dies silently after the chooser pairing — the device never appears, and the embedded server is dead from that point on. Native btleplug paths are unaffected, so the same device works fine through Intiface Central, which makes this confusing to diagnose from the outside.
Repro
Sensee Capsule (
CCPA10S2, protocolsensee-v2— its initializer reads model data from Tx). Chrome/macOS, chooser pairs fine, then the panic above. Any read-during-init protocol should reproduce.Fix
Build the view over the underlying buffer, same as the Subscribe handler a few lines below already does:
Patched in our local wasm build and verified against a physical Sensee Capsule (CCPA10S2): device now identifies and runs (vibrate + constrict) through the browser embedded server. Happy to open a PR with this if you'd like (opening the issue first per the contribution policy).