A Bluetooth session that lost its companion could not get it back, and the
reason was one call that four separate layers each declined to make (JP,
2026-09-04).
When the peripheral is what goes away, bleak fires `BLEConnection`'s
dropped-link callback, which restores the connection's fields to what the
caller originally passed — setting `client` back to None and letting go of the
live object. `BLEConnection.disconnect` then guards on `self.client and
self.client.is_connected` and has nothing to close; `ConnectionManager
.disconnect` guards on a flag its own drop handler already cleared;
`_force_close_transport` reaches past both, but only as far as that same
`BLEConnection`, whose disconnect is the no-op above. Every guard is locally
right and the sum of them is that `BleakClient.disconnect()` is never called
on a link that dropped by itself.
On Windows the WinRT `BluetoothLEDevice` and its GATT session then stay open
for the life of the process, and every later connect to that address is served
a broken service table: the link comes up and the UART subscribe fails with
"Characteristic 6E400003-… was not found!". The device advertises at -61 dBm,
answers a hand-written APP_START over raw GATT with a valid SELF_INFO, and is
unreachable to this app until it restarts — while a fresh process connects to
it in 0.6s. That asymmetry is the whole tell, and it is what turned every
reconnect attempt into a fast, inexplicable failure.
So the client is ours now: taken at connect while the library still has one to
give, and closed unconditionally at teardown — never guarded on `is_connected`,
because the case that matters is exactly the one where it is already False.
Bounded and silent, since a teardown may not raise or hang, and a no-op on the
transports that have no client to hold.
Measured on the real companion: the session drops, is heard again 7.2s after
the device comes back, and is rebuilt 0.97s later — 8.3s from drop to live,
where it previously never recovered at all.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>