Repository navigation
fix: report a stale bond on iPhones that only see the device disconnect (OK-63686) - #953
Merged
Merged
Conversation
huhuanming
approved these changes
Sep 21, 2026
wabicai
approved these changes
Sep 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On an iPhone 17 Pro, reconnecting to a Pro 2 that was wiped ends in "Device not found" (105) instead of the pairing-invalid guidance. This reports
BleBondInvalid(724) for that case, so the app shows its existing "forget this device" dialog. Releases all packages as1.2.4(1.2.4-alpha.2was the build tested on the device; same code).OK-63686
What happens on the phone
From an app log of an iPhone 17 Pro (iOS 26.6.2), 11 attempts out of 11 after a device wipe:
CBErrorPeerRemovedPairingInformation(14), which already maps to 719. The iPhone 17 Pro only reportsCBErrorPeripheralDisconnected(7) on the operation that was in flight. A third-party scanner app gets the same code from the same device, so nothing the transport sends causes or avoids it. Pro 2 firmware drops a link on its second key failure, which is the likely source of the disconnect.discoverAllServicesAndCharacteristics), about 25 ms after the connect. That raw ble-plx error is not classified, so Core retries it and then answersDeviceNotFound.1.2.3-alpha.11shows the identical sequence and no[BleBondDiagnostic]line. Its later revision (1.2.4-alpha.0) keeps that evidence across retries but still records it only in the probes.{ stage: 'gatt-setup', errorCode: 201, iosErrorCode: 7 }.Change
bleIosStaleBond.tsreads the native code from the failed operation, the only place it appears on iOS (the disconnect event carries no reason). It is noted at the three places a native error can surface before the first response: GATT setup, the notification stream, and the packet writer used by both protocol probes.A device that reboots or powers off right after connecting ends one link the same way, so:
BleBondInvalidwithparams: { phase: 'connect', reason: 'peer_disconnected' }. Both must be code 7, on a link that attempt opened, within 10 s of connecting and before any response, and at most 60 s apart;skipProtocolProbe) are never counted, becauseFirmwareUpdateV4treats stale-bond codes as terminal.CBError14 during GATT setup now maps to 719 as it does on connect and on writes.Each of those failures now logs its native codes (
[ReactNativeBleTransport] iOS operation failed { stage, errorCode, iosErrorCode, attErrorCode }), so the device test shows what the phone really reports even if the verdict does not fire. Codes only; no reason text or identifier beyond the existing 8-character suffix.iOS only. Android and the other transports are untouched, and an iPhone that reports code 14 behaves as before.
Limits
retryCount: 0makes a single attempt, so its first failure is still "Device not found" and the next call reports 724. The onboarding connect usesretryCount: 1and gets 724 directly.Test plan
iosPeerTermination.test.ts: 11 tests drivingacquire()with the logged error shape — first attempt keeps its error, second reports 724 with params that survive serialization; drops seen by the write path and by the notification stream; expiry; reset after another failure and after a success; firmware-install reconnect; code 14 in GATT setup. Each hook was removed in turn and the matching test failed.tsc --noEmitclean for the package.6130048a2ewith SDK1.2.4-alpha.2(fix: show the pairing-invalid dialog on iPhones that only see the device disconnect (OK-63686) app-monorepo#13678). Three wipe-and-reconnect cycles: each onboarding connect made two attempts, both ended in GATT setup with native code 7, and the call returned 724 withparams: { phase: 'connect', reason: 'peer_disconnected' }in about 2.9 s; no 105 in that build's log. After forgetting the device in iOS settings, the reconnect paired and the following calls succeeded.