| name | debug-rn-native-crash |
| description | Investigate native React Native crashes (Fabric/Hermes/iOS) in ledger-live-mobile when JS error logs are missing or unhelpful. Use when the app dies silently, throws an NSException on iOS, or shows only a native stack trace. |
Debug RN native crash
Trigger
The mobile app crashes with one of:
- A native iOS exception (
NSInternalInconsistencyException, RCTFatal, Fabric assertion)
- Only a native stack trace (
RCTViewComponentView, RCTPerformMountInstructions, …) with no JS source
- "Failed to symbolicate" in Metro and no actionable JS error
- Reproducible crash but unclear which RN component is at fault
If the JS console already gives a clear stack, don't use this skill — read the JS error.
Workflow
1. Capture the native exception from the simulator
xcrun simctl list devices booted
xcrun simctl spawn <UDID> log show --last 5m --style compact \
--predicate 'processImagePath CONTAINS "ledgerlivemobile" AND (eventMessage CONTAINS "Assertion failure" OR eventMessage CONTAINS "Attempt to unmount" OR eventMessage CONTAINS "Terminating app")'
Extract the exception message, native stack, and any view descriptors (frame, tag, backgroundColor, alpha). These already tell you a lot — see reference.md "Anatomy of a Fabric assertion message".
2. Reproduce under Xcode debugger
open apps/ledger-live-mobile/ios/ledgerlivemobile.xcworkspace
- Stop the running app first (otherwise Xcode can't attach cleanly).
- In Xcode: Cmd+8 → + → Exception Breakpoint → Objective-C → All.
- Cmd+R to build + run + attach.
- Reproduce the crash. Xcode pauses at
objc_exception_throw.
3. Identify the offending React component
In the lldb console at the bottom of Xcode:
- Pick the frame that has the offending C++/ObjC code:
bt # full backtrace, find the frame in RN code
frame select N # N = the RN frame (often RCTPerformMountInstructions)
- Inspect the views referenced by the failed instruction. Variable names depend on the frame — read the source displayed in Xcode. Common ones:
po parentViewDescriptor.view
po oldChildViewDescriptor.view
p mutation.parentTag
p mutation.index
p oldChildShadowView.componentName
- Find where the orphan child is actually attached vs where Fabric thinks:
po [oldChildViewDescriptor.view superview]
po [oldChildViewDescriptor.view accessibilityIdentifier] # = RN testID
po [parentViewDescriptor.view superview]
po [[parentViewDescriptor.view superview] accessibilityIdentifier]
po [parentViewDescriptor.view subviews]
- Climb the superview chain until you hit a
accessibilityIdentifier (= RN testID) you recognise in source. Grep for it:
grep -rn 'testID="<identifier>"' apps/ledger-live-mobile/src libs/ui
That's the RN component family. From there, read the JSX and find the conditional render / animation / library that produced the mismatch.
4. Map the native cause to a code pattern
The vast majority of Fabric assertions come from a small set of patterns. See reference.md "Common Fabric crash patterns".
5. Fix and verify Metro actually serves the fix
A common trap: Metro's file watcher dies on EMFILE: too many open files. Edits silently never reach the bundle. Always confirm Metro logged a fresh Compiled ios in Xs line after each edit (in the terminal running pnpm mobile start:ios, or wherever Metro's stdout is streaming).
If the watcher is broken, restart Metro with --reset-cache. Reload the app (Cmd+R in sim or shake → Reload).
Guardrails
- Don't guess at causes from the JS code alone — the native debugger is the source of truth.
- Don't trust "the bug only happens when flag X is on" until the user has tested both states. Correlation in mobile apps is often coincidental (cold start vs hot reload, cache state, etc.).
- Don't apply five fixes at once. Apply one, verify Metro served it, retest. Each silent failed iteration is wasted hours.
Output
- The exact React component identified as the orphan (from
accessibilityIdentifier / testID)
- The pattern name (e.g. "Reanimated
withRepeat on unmount", "unstable keyExtractor")
- The minimal fix applied
- Confirmation Metro served the fix and the crash no longer reproduces
Reference
See reference.md in this folder for:
- Anatomy of a Fabric assertion message
- lldb cheatsheet for Fabric mount errors
- Catalogue of common Fabric crash patterns with code-side fixes