Skip to content

create_big_sync(): KeyError instead of HCI_Error hides the BIG sync failure status #954

Description

@devitwise

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):

  1. 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)
  2. That emit resolves the established future with HCI_Error(status).
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions