v157 · web bluetooth · needs hardware for the full path

Reconnect lifecycle

The spec marks device.gatt as [SameObject]: every read returns the same server instance, across disconnects and reconnects. Combined with the server now being an EventTarget, that yields the killer property of this design — attach a connection-level listener once and it survives every reconnect. This page proves the identity on real hardware and demonstrates the backoff-reconnect pattern that relies on it.

Prerequisites

probing…

Prove it on a device

Opens Chrome's device chooser. After connecting, use "Drop & auto-reconnect" (or power-cycle the device) and watch: the reconnect resolves the same server object (identity check runs each cycle), and the listener attached before the first drop keeps firing after reconnection without being re-attached.

device.gatt === first ref
reconnect cycles
0
listener re-attached?
never (that's the point)
server.connected

Log

the pattern

const device = await navigator.bluetooth.requestDevice({ acceptAllDevices: true });
const server = device.gatt;          // [SameObject] — stable for the device's life

// Attach ONCE. Survives every reconnect because the object survives.
server.addEventListener("maxwritewithoutresponsesizechanged", onSizeChange);

device.addEventListener("gattserverdisconnected", async () => {
  for (let delay = 250; delay <= 8000; delay *= 2) {   // exponential backoff
    await new Promise((r) => setTimeout(r, delay));
    try {
      await server.connect();        // same object, fresh connection
      return;                        // listener above is still live
    } catch { /* next backoff step */ }
  }
  showOffline();
});

// The stale-listener bug this design prevents: with a NEW server object
// per connection you would re-attach listeners each time — forget one
// path and a renegotiated write size silently truncates your queue.

see also