fix: Ledger "Something went wrong" error on reconnect during send/sign#28316
fix: Ledger "Something went wrong" error on reconnect during send/sign#28316Copilot wants to merge 3 commits into
Conversation
Three related bugs fixed: 1. useDeviceConnectionFlow: retryEnsureDeviceReady now uses the device ID stored in pendingTargetDeviceIdRef (set at the start of ensureDeviceReady) when state.deviceId is null. Previously, if the initial BLE open failed before DeviceEvent.Connected could set state.deviceId (or on a fresh app launch), the retry fell through to scanning mode instead of connecting directly to the known device. This was the primary cause of the 'something went wrong' error — the scan would fail with an unrecognized error code. 2. LedgerBluetoothAdapter: #doEnsureDeviceReady now throws a named DisconnectedDevice error (instead of silently returning false) when the transport is null after connect(). The silent false return left the UI stuck at 'Connecting' with no feedback. 3. LedgerBluetoothAdapter: #verifyEthereumAppUnlocked now throws a named DisconnectedDeviceDuringOperation error (instead of generic 'Transport not available') when the transport is null. The generic error did not match any pattern in parseErrorByType, so it was surfaced as ErrorCode.Unknown → 'Something went wrong'. The named error is both transient (retried automatically) and maps to DeviceDisconnected if all retries are exhausted. Agent-Logs-Url: https://github.com/MetaMask/metamask-mobile/sessions/fa70675e-fa60-4403-a6eb-2b751536532a Co-authored-by: dawnseeker8 <7315988+dawnseeker8@users.noreply.github.com>
|
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
🔍 Smart E2E Test Selection
click to see 🤖 AI reasoning detailsE2E Test Selection:
These changes are well-contained within the hardware wallet connection layer. The The changes are low-risk in terms of breaking other flows since they only affect the Ledger BLE connection retry logic. The unit tests have been updated to match the new behavior. Confidence is moderate (72) because there are no direct E2E tests for Ledger BLE flows, making it hard to fully validate the fix through automated testing. Performance Test Selection: |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit aa97022. Configure here.
| } else { | ||
| // Remember the device ID so retryEnsureDeviceReady can use it even | ||
| // when state.deviceId has not been populated yet. | ||
| pendingTargetDeviceIdRef.current = targetDeviceId; |
There was a problem hiding this comment.
Stale pendingTargetDeviceIdRef not cleared for no-device flows
Medium Severity
When ensureDeviceReady is called without a targetDeviceId (falsy), pendingTargetDeviceIdRef is not cleared. If a previous call stored a device ID in the ref, a subsequent retryEnsureDeviceReady will use the stale value via deviceId ?? pendingTargetDeviceIdRef.current instead of falling through to scanning mode. The if (!targetDeviceId) branch needs to set pendingTargetDeviceIdRef.current = null alongside setDeviceId(null).
Additional Locations (1)
Reviewed by Cursor Bugbot for commit aa97022. Configure here.
|
Duplicate PR to #28515 |


After a Ledger disconnects mid-signing and the user reconnects, tapping "Continue" shows "Something went wrong" instead of proceeding to sign. Three related bugs compound to cause this.
Root causes & fixes
useDeviceConnectionFlow— wrong retry path whenstate.deviceIdis nullretryEnsureDeviceReadyrelies onstate.deviceIdto know which device to reconnect. If the initial BLE open failed beforeDeviceEvent.Connectedfired (device already off when Confirm was tapped, or fresh app launch),state.deviceIdis never populated and retry silently falls into scanning mode. A scan failure with an unrecognised error code then surfaces asErrorCode.Unknown→ "Something went wrong".Fix: Add
pendingTargetDeviceIdRef— set fromtargetDeviceIdat the top ofensureDeviceReady.retryEnsureDeviceReadynow usesdeviceId ?? pendingTargetDeviceIdRef.currentso it always reconnects directly to the correct device.LedgerBluetoothAdapter#doEnsureDeviceReady— silentreturn falseon null transportWhen the transport is cleared by a disconnect event immediately after
connect(), the method returnedfalsewith no error, leaving the UI stuck at "Connecting" indefinitely.Fix: Throw a named
DisconnectedDeviceerror so the retry loop inensureDeviceReadyhandles it as transient and retries automatically.LedgerBluetoothAdapter#verifyEthereumAppUnlocked— unrecognised error surfaced as "Something went wrong"throw new Error('Transport not available')uses a generic name/message that matches no entry inERROR_NAME_MAPPINGSand no pattern inparseErrorByMessage, soparseErrorByTypefalls through toErrorCode.Unknown.Fix: Throw a named
DisconnectedDeviceDuringOperationerror — transient (auto-retried up to 3×) and maps toErrorCode.DeviceDisconnectedif all retries fail, giving the user an actionable message.Note
Medium Risk
Changes control flow and error classification in Ledger connection/readiness checks; could alter retry behavior and surfaced errors during BLE instability or app-switching.
Overview
Fixes Ledger BLE reconnect flows so retrying after a mid-sign disconnect resumes signing instead of falling back to scanning or surfacing a generic "Something went wrong".
useDeviceConnectionFlownow remembers the lastdeviceIdpassed toensureDeviceReadyand uses it inretryEnsureDeviceReadywhenstate.deviceIdis still null, and clears that stored ID oncloseFlow.LedgerBluetoothAdapternow throws named disconnect errors when#transportis unexpectedly null afterconnect()or during Ethereum-app verification so the existing retry loop treats these as transient (tests updated accordingly).Reviewed by Cursor Bugbot for commit aa97022. Bugbot is set up for automated code reviews on this repo. Configure here.