Skip to content

Crash after BluetoothRemoteGATTServer.disconnect() (async TypeError in PeripheralHandles.deleteHandles) #394

@kayahr

Description

@kayahr

Description

After calling disconnect() on a connected BluetoothRemoteGATTServer, the process crashes asynchronously on the next event-loop turn. disconnect() itself does not throw. The error is not catchable from userland because it happens inside the adapter cleanup code and results in an unhandled rejection / process abort.

Reproduction

import { bluetooth } from "webbluetooth";

const SERVICE_UUID = "…"; // any service your peripheral advertises

function nextTick(): Promise<void> {
    return new Promise((r) => setTimeout(r, 0));
}

const device = await bluetooth.requestDevice({
    filters: [{ services: [SERVICE_UUID] }],
    optionalServices: [SERVICE_UUID],
});
console.log("Device:", device.name);

const server = await device.gatt.connect();
console.log("Connected");

server.disconnect();
console.log("Disconnected");

await nextTick(); // crash happens here
console.log("Exit"); // Never reached

Expected behavior

server.disconnect() should disconnect cleanly without throwing later, and the program should continue normally.

Actual behavior

Crash on next event loop turn:

$ node test.js
Device: BleServer
Connected
Disconnected
/tmp/ble-client/node_modules/webbluetooth/dist/adapters/simpleble-adapter.js:78
    var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
                                                                       ^

TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))
    at __values (/tmp/ble-client/node_modules/webbluetooth/dist/adapters/simpleble-adapter.js:78:72)
    at PeripheralHandles.deleteHandles (/tmp/ble-client/node_modules/webbluetooth/dist/adapters/simpleble-adapter.js:194:35)
    at SimplebleAdapter.<anonymous> (/tmp/ble-client/node_modules/webbluetooth/dist/adapters/simpleble-adapter.js:468:30)
    at step (/tmp/ble-client/node_modules/webbluetooth/dist/adapters/simpleble-adapter.js:72:23)
    at Object.next (/tmp/ble-client/node_modules/webbluetooth/dist/adapters/simpleble-adapter.js:53:53)
    at /tmp/ble-client/node_modules/webbluetooth/dist/adapters/simpleble-adapter.js:47:71
    at new Promise (<anonymous>)
    at __awaiter (/tmp/ble-client/node_modules/webbluetooth/dist/adapters/simpleble-adapter.js:43:12)
    at SimplebleAdapter.disconnect (/tmp/ble-client/node_modules/webbluetooth/dist/adapters/simpleble-adapter.js:457:16)
    at BluetoothRemoteGATTServerImpl.disconnect (/tmp/ble-client/node_modules/webbluetooth/dist/server.js:131:28)

Environment

  • Node.js 24.13.0
  • webbluetooth 3.4.0
  • Debian Linux 13

Additional notes

Looks like PeripheralHandles.deleteHandles() iterates over a collection that can be undefined during disconnect cleanup. A guard for missing handle maps (or initializing them consistently) might fix it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions