Device.create_big_sync() raises KeyError instead of HCI_Error, discarding the BIG sync failure status.
What happens
When BIG sync establishment fails, the big_handle entry is deleted twice, and the resulting KeyError replaces the real HCI status.
On main (checked at 8292-line hci.py / 7026-line device.py):
on_big_sync_establishment() (device.py:5828-5832) takes the status != HCI_SUCCESS branch:
if status != hci.HCI_SUCCESS:
del self.big_syncs[big_handle] # first delete
...
big_sync.emit(BigSync.Event.ESTABLISHMENT_FAILURE, status)
- That emit resolves the
established future with HCI_Error(status).
- Back in
create_big_sync() (device.py:5279-5281), await established raises, and:
except hci.HCI_Error:
del self.big_syncs[big_handle] # second delete -> KeyError
raise
Because KeyError is not an HCI_Error, it escapes the handler and propagates to the caller. The HCI_Error carrying the status is lost.
Impact
The caller sees KeyError: 0 and has no way to learn why the sync failed. In our case the underlying status was CONNECTION_TERMINATED_DUE_TO_MIC_FAILURE_ERROR (0x3D) — a wrong broadcast code — but the exception said nothing about it. We lost a full measurement batch to this: twelve runs were scored as crashes while the actual cause was a changed broadcast code on the transmitting device.
Any BIG sync establishment failure is affected, not just MIC failures.
Same defect in the broadcaster path
create_big() has the identical pattern: device.py:5232 (except HCI_Error: del self.bigs[big_handle]) versus device.py:5777 (on_big_establishment, failure branch). Worth fixing together.
Suggested fix
Make the cleanup idempotent, e.g. self.big_syncs.pop(big_handle, None) in both places — on_big_termination() at device.py:5801 already uses pop(..., None) for exactly this reason.
Workaround for others hitting this
The status is still observable on the host event, before the exception is raised:
failure = []
device.host.on('big_sync_establishment', lambda status, *_: status and failure.append(status))
try:
big_sync = await device.create_big_sync(pa_sync, params)
except Exception:
if failure:
print(hci.HCI_Constant.error_name(failure[0])) # the real reason
raise
Happy to send a PR if the pop(..., None) approach looks right.
Device.create_big_sync()raisesKeyErrorinstead ofHCI_Error, discarding the BIG sync failure status.What happens
When BIG sync establishment fails, the
big_handleentry is deleted twice, and the resultingKeyErrorreplaces the real HCI status.On
main(checked at 8292-linehci.py/ 7026-linedevice.py):on_big_sync_establishment()(device.py:5828-5832) takes thestatus != HCI_SUCCESSbranch:establishedfuture withHCI_Error(status).create_big_sync()(device.py:5279-5281),await establishedraises, and:Because
KeyErroris not anHCI_Error, it escapes the handler and propagates to the caller. TheHCI_Errorcarrying the status is lost.Impact
The caller sees
KeyError: 0and has no way to learn why the sync failed. In our case the underlying status wasCONNECTION_TERMINATED_DUE_TO_MIC_FAILURE_ERROR (0x3D)— a wrong broadcast code — but the exception said nothing about it. We lost a full measurement batch to this: twelve runs were scored as crashes while the actual cause was a changed broadcast code on the transmitting device.Any BIG sync establishment failure is affected, not just MIC failures.
Same defect in the broadcaster path
create_big()has the identical pattern:device.py:5232(except HCI_Error: del self.bigs[big_handle]) versusdevice.py:5777(on_big_establishment, failure branch). Worth fixing together.Suggested fix
Make the cleanup idempotent, e.g.
self.big_syncs.pop(big_handle, None)in both places —on_big_termination()atdevice.py:5801already usespop(..., None)for exactly this reason.Workaround for others hitting this
The status is still observable on the host event, before the exception is raised:
Happy to send a PR if the
pop(..., None)approach looks right.