Skip to content

fix(webbluetooth): read characteristic values via the DataView's buffer - #942

Open
cfanboy wants to merge 1 commit into
buttplugio:masterfrom
cfanboy:fix/webbluetooth-read-dataview
Open

fix(webbluetooth): read characteristic values via the DataView's buffer#942
cfanboy wants to merge 1 commit into
buttplugio:masterfrom
cfanboy:fix/webbluetooth-read-dataview

Conversation

@cfanboy

@cfanboy cfanboy commented Aug 22, 2026

Copy link
Copy Markdown

Fixes #941 (maintainer green-lit the PR in the issue thread).

Problem

In the WebBluetoothDeviceCommand::Read handler, readValue() resolves to a DataView, but the bytes were extracted with Uint8Array::new(&data_view). In JS, new Uint8Array(dataView) treats the DataView as a plain array-like (length === undefined), producing an empty array. The subsequent copy_to then trips js-sys's length assertion and panics, killing 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 protocol that performs a hardware read during init dies silently right after chooser pairing, and the embedded server is dead from that point on. Native btleplug paths are unaffected, which makes this confusing to diagnose from the outside.

Fix

Build the Uint8Array over the DataView's underlying buffer(), honoring its byte offset/length — the Subscribe notification handler already uses the buffer-based pattern. One expression changed, plus a comment explaining why Uint8Array::new must not be used here.

Verification

  • cargo check -p buttplug_server_hwmgr_webbluetooth --target wasm32-unknown-unknown passes.
  • Verified against a physical Sensee Capsule (CCPA10S2, protocol sensee-v2, which reads model data from Tx during init) on Chrome/macOS: with this patch in our wasm build, the device identifies and runs (vibrate + constrict) through the browser embedded server; without it, the panic above reproduces 100% after pairing.

`readValue()` resolves to a DataView. `Uint8Array::new(&data_view)`
treats it as a plain array-like (length undefined) and yields an empty
array, so the subsequent `copy_to` length assertion panics and kills
the wasm instance. Any protocol that performs a hardware read during
init (e.g. sensee-v2) dies right after chooser pairing.

Build the Uint8Array over the DataView's underlying buffer instead,
honoring its byte offset/length — the Subscribe notification handler
already uses the buffer-based pattern.

Verified against a physical Sensee Capsule (CCPA10S2) via Chrome/macOS:
the device identifies and runs through the browser embedded server.

Fixes buttplugio#941

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QitzJcyNqb1CHnMJ8rcZP1
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


UPPower seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WebBluetooth: any characteristic read panics the wasm engine — Uint8Array::new(DataView) yields an empty array

2 participants