Bug report: BLE.scanWithFilter() callback overload appears unsafe in Device OS 6.3.5

Platform: Particle MSOM, Device OS 6.3.5

API: BleLocalDevice::scanWithFilter(const BleScanFilter&, const BleOnScanResultStdFunction&) and the function-pointer overloads of the same.

There's a problem with scanWithFilter when used with a callback: Callbacks are silently dropped — the scan runs for its full timeout, returns successfully, but the user-supplied callback is never invoked, even though adverts matching the (empty) filter are demonstrably being received by the HAL.

The vector-returning overload Vector<BleScanResult> scanWithFilter(const BleScanFilter&) works correctly under the same conditions.


Example

BleScanFilter scanFilter;
scanFilter.allowDuplicates(true);   // critical — see below

BLE.setScanTimeout(500);            // 5 s

// (A) callback overload — broken: callback never fires
BLE.scanWithFilter(scanFilter, [&](const BleScanResult& result) {
    Log.info("got advert from %s", result.address().toString().c_str());
});

// (B) vector overload — works: all adverts present in returned vector
Vector<BleScanResult> results = BLE.scanWithFilter(scanFilter);

Both forms use the same BleScanDelegator machinery in wiring/src/spark_wiring_ble.cpp. The only difference along the failing path is that start(const BleOnScanResultStdFunction& callback) assigns scanResultCallbackRef_ = callback; before calling hal_ble_gap_start_scan(...). Adverts received by the HAL during the scan reach onScanResultCallback, but scanResultCallbackRef_(result) is either not invoked or invokes an invalid functor.

A workload that reliably triggers (A): scanning for 5 s with 2 BLE peers in range that each advertise on a ~1 Hz cadence. The vector overload returns ~20–30 results; the callback overload returns 0 callback invocations.