Skip to content

fix(corebluetooth): ignore service rediscovery nobody requested - #479

Open
grmrgecko wants to merge 1 commit into
deviceplug:masterfrom
grmrgecko:fix/corebluetooth-late-rediscovery
Open

grmrgecko wants to merge 1 commit into
deviceplug:masterfrom
grmrgecko:fix/corebluetooth-late-rediscovery

Conversation

@grmrgecko

@grmrgecko grmrgecko commented Sep 22, 2026

Copy link
Copy Markdown

On macOS, CoreBluetooth rediscovers a peripheral's services on its own, for instance when the peripheral indicates Service Changed. The descriptor callbacks for that rediscovery reach PeripheralInternal::check_discovered after the reply to the original discover_services request has gone out. services_discovered_future_state is None, and this panics:

thread '<unnamed>' panicked at src/corebluetooth/internal.rs:346:17:
We should still have a future at this point!

The panic kills the CoreBluetooth thread, so every later central operation in the process fails until it restarts.

I ran into it on 0.13.2, connecting from macOS to a Linux machine acting as a BLE MIDI peripheral through BlueZ. BlueZ registers the peripheral's GATT application after the connection is up. The first discovery succeeded, and the rediscovery that followed panicked. #223 reported the same message in 2022 from a different trigger.

By the time check_discovered runs, set_characteristics and set_characteristic_descriptors have already updated the service map, so a rediscovery with no pending request has nothing to reply to. It now logs at trace level and returns. The is_none() check and the later take().unwrap() become one let Some(..) = take() else.

rediscovery_without_a_pending_request_is_ignored uses the same setup as descriptor_discovery_error_completes_service_discovery_without_descriptors. It completes descriptor discovery with no pending request, which panicked before this change, then checks that a request made afterwards still gets its reply. With the change applied, the macOS central connected to the BlueZ peripheral and exchanged MIDI in both directions.

CoreBluetooth rediscovers a peripheral's services on its own, for
instance when the peripheral indicates Service Changed. The descriptor
callbacks for that rediscovery reach check_discovered after the reply to
the original discover_services request has gone out. No future is
waiting, and the panic ("We should still have a future at this point!")
kills the CoreBluetooth thread along with every later central operation.

Seen connecting from macOS to a BlueZ peripheral that registers its GATT
application after the connection. The first discovery succeeded, and
the rediscovery that followed panicked.

The service map is already up to date when check_discovered runs, so a
rediscovery with no pending request now logs at trace level and
returns. The regression test completes descriptor discovery with no
pending request, then checks that a later request still gets its reply.
@grmrgecko
grmrgecko force-pushed the fix/corebluetooth-late-rediscovery branch from f79a356 to 3a9da4b Compare September 22, 2026 03:03
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.

1 participant