AI-powered crash analysis is now available on all plans — including Free.Read the crash analysis guide

React Native App Crashing on iOS: Common Causes and Fixes (2026)

NFNourin Mahfuj Finick··8 min read

iOS crashes in React Native fall into a few categories: JavaScript exceptions (same as Android), native module crashes (Objective-C/Swift exceptions), memory-related process kills, and system-level crashes like watchdog timeouts. Each type leaves a different fingerprint in your crash reports.


This guide covers how to identify which type you're dealing with and how to fix the most common patterns.


Reading iOS Crash Reports


iOS crash logs (from Xcode Organizer, App Store Connect, or your crash reporter) contain a few key sections:


Exception Type — The OS-level exception that caused the termination:

  • EXC_CRASH (SIGABRT) — Abort signal, usually from an Objective-C exception or a failed assertion
  • EXC_BAD_ACCESS (SIGSEGV) — Memory access violation (use-after-free, buffer overflow)
  • EXC_BAD_ACCESS (SIGBUS) — Misaligned memory access
  • EXC_RESOURCE — Process exceeded CPU/memory limits
  • SIGKILL — Process was killed by the OS (memory pressure, watchdog)

Termination reason for SIGKILL:

  • Memory limit exceeded — OOM kill
  • Watchdog — App was unresponsive during launch or in foreground (exceeded 20s or 5s respectively)
  • Background task timeout — Background task ran too long

Type 1: JavaScript Crashes (RCTFatalException)


Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Application Specific Information:
  RCTFatalException: Unhandled JS Exception:
  TypeError: Cannot read property 'id' of undefined

These are JS exceptions that weren't caught by your ErrorBoundary or global error handler and propagated to the native layer. The fix is the same as any JS crash — add error handling in the JS code and ensure your ErrorBoundary is wrapping the full app.


Type 2: Native Module Crashes (Objective-C Exceptions)


Exception Type: EXC_CRASH (SIGABRT)
Application Specific Information:
  *** Terminating app due to uncaught exception 'NSInternalInconsistencyException',
  reason: 'UITableView internal inconsistency...'

These come from native modules that throw Objective-C exceptions. Common causes:


UIKit called from background thread:

React Native's bridge occasionally routes UI operations to a background thread in certain conditions. Any UIKit call from a non-main thread causes this crash.


// WRONG — manipulating UI state from a callback that may run on background thread
someNativeModule.onEvent((data) => {
  dispatch(updateState(data)); // may cause UIKit background thread crash
});

// CORRECT — ensure state updates run on main thread
import { InteractionManager } from 'react-native';
someNativeModule.onEvent((data) => {
  InteractionManager.runAfterInteractions(() => {
    dispatch(updateState(data));
  });
});

Collection mutated during enumeration:

A native module iterates an array while JS code simultaneously modifies it through the bridge.


Type 3: Memory Pressure Kills (SIGKILL — Memory limit exceeded)


iOS silently kills your process when memory exceeds a device-specific threshold (roughly 1–1.5GB on modern devices, 200–400MB on older devices). There's no crash log on the JS side — the process just terminates.


Identify these in your crash reporter by looking for sessions that end without a JavaScript exception — particularly sessions that had memory warning breadcrumbs beforehand.


Prevention strategies:

  • Clear image caches on memory warning (FastImage.clearMemoryCache())
  • Release large data structures when the app backgrounds (AppState listener)
  • Avoid loading all pages of a paginated list into memory simultaneously
  • Use FlatList's removeClippedSubviews and low maxToRenderPerBatch values

Type 4: Watchdog Kills (SIGKILL — Watchdog)


iOS kills your app if it doesn't finish launching within ~20 seconds or becomes unresponsive in the foreground for ~5 seconds.


Launch watchdog — Too much synchronous work on the main thread during app startup. Common causes: synchronous SQLite queries, synchronous file I/O, or synchronous network calls in your initialization path.


// WRONG — synchronous init work blocks launch
const config = fs.readFileSync(configPath, 'utf8'); // blocks main thread

// CORRECT — async everything during launch
const config = await fs.readFile(configPath, 'utf8');

Foreground watchdog — A long-running synchronous operation on the main thread. Move heavy computation off the main thread (use InteractionManager or native background queues).


Type 5: Swift/ObjC Interop Crashes


If you use Swift-based native modules, Swift exceptions (which aren't Objective-C exceptions) propagate differently and can cause unexpected crashes.


// Swift force-unwrap crash
@objc func doSomething(_ resolve: RCTPromiseResolveBlock) {
  let value = self.optionalValue! // crashes if nil — doesn't surface as an ObjC exception
}

// CORRECT — use guard or optional binding
@objc func doSomething(_ resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) {
  guard let value = self.optionalValue else {
    reject("NO_VALUE", "Value was nil", nil)
    return
  }
  resolve(value)
}

Debugging with Device Logs


For crashes you can't reproduce via a crash reporter, attach a physical iOS device and check the device logs:


# Stream device logs (requires Xcode command line tools)
xcrun devicectl device info processes --device [device-id]

# Or use Console.app on macOS — filter by your app bundle ID

This is especially useful for watchdog kills and memory crashes that don't produce JS-side reports.


Symbolication


iOS crash logs contain memory addresses instead of function names unless you have the dSYM file for your build. Upload your dSYM to your crash reporter in your CI/CD pipeline:


# After building iOS release
npx bugspulse-cli upload-dsym   --api-key bp_your_key   --version 2.4.1   --dsym-path ./ios/build/Products/Release-iphoneos/YourApp.app.dSYM

Without dSYM upload, native iOS crash traces are unreadable hex addresses.