sodcalculator

10 Tech How-To

React Native Android Crash: Find the Native Fault

Guide 10-37237 · Updated · Step by step

To find a native fault in a React Native Android crash, you need to read the tombstone or the backtrace from logcat, then map the native symbols to your app’s code using the debug symbols from the build. This article explains how to capture, decode, and trace these crashes to their root cause.

What Is a Native Fault?

React Native apps run JavaScript on a separate thread, but the core rendering, networking, and many libraries (like those using C/C++) execute natively. A native fault occurs when this native code crashes—for example, a null pointer dereference, an out-of-bounds array access, or a failed assertion. Unlike a JavaScript error, which shows a red box in development, a native crash usually terminates the app immediately and produces a tombstone file on Android.

Capturing the Crash Log

When a native crash happens, Android writes a tombstone to /data/tombstones and prints a stack trace to logcat. To capture it, connect the device via USB and run:

adb logcat -v threadtime *:V

Reproduce the crash. The relevant lines will include F DEBUG or F libc and a backtrace with addresses like #00 pc 0x0000000000001234. If you cannot reproduce it easily, check adb shell ls /data/tombstones and pull the latest file with adb pull /data/tombstones/tombstone_00.

Reading the Backtrace

Each line in the backtrace shows a frame number, the program counter address, the library name, and the offset. For example:

#00 pc 0x0000000000001a2b /data/app/.../lib/arm64/libnative.so

This tells you the crash happened inside libnative.so at offset 0x1a2b. The library name is the key—it tells you which module is at fault. If it’s libreactnative.so, the problem is likely in the React Native core; if it’s a third-party library like librealm.so, the issue is there.

Symbolicating the Stack Trace

Raw addresses are useless without symbols. To get function names, you need the unstripped .so files from your build. For a debug build, these are in android/app/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/. For a release build, you must enable symbol generation in your build.gradle:

android {
    buildTypes {
        release {
            ndk {
                debugSymbolLevel 'FULL'
            }
        }
    }
}

Then use the Android NDK’s ndk-stack tool to symbolize the logcat output:

adb logcat -d > crash.log
ndk-stack -sym build/app/intermediates/merged_native_libs/release/mergeReleaseNativeLibs/out/lib/ -dump crash.log

This replaces addresses with function names and line numbers, making the crash location clear.

Common Causes and Fixes

  • Third-party native libraries—update them to the latest version, as bugs are often fixed.
  • Memory issues—large images or data can cause out-of-memory crashes; use android:largeHeap="true" only as a temporary fix.
  • Architecture mismatch—if you only build for arm64-v8a, older devices will crash; include armeabi-v7a and x86 in your abiFilters.
  • Release vs debug—some crashes only appear in release due to ProGuard/R8 stripping; keep debug symbols for all builds.

Using Breakpad for Production Crashes

For crashes that only happen on user devices, you need a crash reporting tool that captures native stacks. Google’s Breakpad is the standard—it generates minidump files that you can symbolize later. Integrate it via a library like react-native-nativelog or react-native-crash-report. When a crash occurs, the minidump is uploaded to your server, and you can use minidump_stackwalk with your symbols to get the full stack.

Frequently Asked Questions

Why does my app crash only on Android, not iOS?

Android and iOS use different native architectures and memory management. A common reason is that Android devices have varying GPU capabilities and memory limits. Also, some libraries are not fully compatible with Android’s JNI (Java Native Interface) layer, causing crashes only on that platform.

How do I get the debug symbols for a release build?

Set debugSymbolLevel 'FULL' in your release build type. This produces .so files with symbols in the merged_native_libs folder. Alternatively, use android:extractNativeLibs="true" in your manifest to keep the .so files uncompressed, making them easier to extract from the APK.

What does “signal 11 (SIGSEGV)” mean?

It means a segmentation fault—the app tried to access memory it doesn’t have permission to. This is usually a null pointer dereference or a use-after-free bug in native code. Check the backtrace for the exact line that caused it.

Can I prevent native crashes?

Not entirely, but you can reduce them by keeping all native libraries updated, testing on multiple Android versions and screen sizes, and using tools like Firebase Crashlytics to monitor crashes in production. Also, avoid using dangerouslySetInnerHTML or passing large objects between JS and native, as these can trigger memory issues.

Is there a way to see the JavaScript stack in a native crash?

Sometimes. If the crash happens in a bridge call, the native stack may include a reference to a JS function. You can also enable ReactMarker logs to see the last JS operation before the crash. However, often the native stack is enough to pinpoint the library.

Final Thoughts

Native crashes are intimidating but solvable. The key is to always keep debug symbols, use a crash reporting tool in production, and understand how to read a tombstone. With practice, you’ll be able to identify the faulty library and fix the issue quickly.

Related

General steps for current mainstream versions; menus can differ by device and version. See the disclaimer or report an error.

More in this bed

The whole Tech How-To bed →