Skip to content

iOS: Unguarded NSExceptions in TurboModule bridge files cause fatal crashes (SIGABRT/SIGSEGV) under New Architecture #1134

@swey

Description

@swey

Stack trace

Crash 1 — SIGABRT (Thread 31):

__pthread_kill + 8
pthread_kill + 268
abort + 124
demangling_terminate_handler() + 316
_objc_terminate() + 172
std::__terminate() + 16
__cxa_rethrow + 188
objc_exception_rethrow + 44
ObjCTurboModule::performVoidMethodInvocation(...) + 200 (RCTTurboModule.mm:444)

Crash 2 — SIGSEGV (Thread 24):

hermes (internal VM code — wild pointer dereference at 0x45ef13b48)
jsi::Object::setPropertyValue() + 28 (jsi-inl.h:126)
jsi::Object::setProperty() + 112
convertNSExceptionToJSError() + 184 (RCTTurboModule.mm:225)
ObjCTurboModule::performVoidMethodInvocation() + 336 (RCTTurboModule.mm:441)

Reproduction steps

No deterministic reproduction. The crash occurs in production when any Datadog TurboModule method (DdSdk, DdRum, DdLogs, DdTrace) throws an NSException on the shared methodQueue (dd-react-native-sdk). One known trigger is NSInternalInconsistencyException from sendEventWithName:body: (see #854), but any NSException from the Swift implementation layer will cause this.

Under React Native New Architecture, RCTTurboModule.mm's performVoidMethodInvocation catches NSExceptions and either rethrows them as C++ (-> SIGABRT) or calls convertNSExceptionToJSError(runtime, ...) from the wrong thread (-> SIGSEGV). This is a known RN bug (facebook/react-native#53960, #54859).

However, the Datadog bridge files (DdSdk.mm, DdRum.mm, DdLogs.mm, DdTrace.mm) don't have any @try/@catch guards around calls to the Swift implementation layer. Adding defensive exception handling in the bridge would prevent NSExceptions from reaching RCTTurboModule.mm's crash-prone handler — regardless of whether React Native fixes the upstream bug.

Suggested fix: Wrap Swift bridge calls in each .mm file with:

@try {
    [self.ddSdkImplementation someMethod:...];
} @catch (NSException *exception) {
    // Log via SDK telemetry instead of propagating
}

Volume

~2-3% of sessions on iOS, concentrated on devices running iOS 18.5+ and iOS 26.x.

Affected SDK versions

2.8.2 (confirmed), likely all 2.x versions with New Architecture enabled

Latest working SDK version

Unknown — issue exists since New Architecture adoption

Does the crash manifest in the latest SDK version?

Yes (verified bridge files in 2.14.2 — no exception guards present)

React Native Version

0.79.6 (Expo ~53.0, New Architecture enabled, Hermes)

Device Information

  • iPhone 12,8 / iOS 18.5 (Crash 1 — SIGABRT)
  • iPhone 15,4 / iOS 26.2.1 (Crash 2 — SIGSEGV)
  • Multiple crash instances across different devices and OS versions

Other relevant information

Related: #854 (NSInternalInconsistencyException from DdSdkSessionStartedListener)

Related upstream: facebook/react-native#53960, facebook/react-native#54859

While the root cause is in React Native's TurboModule infrastructure, a monitoring SDK should never crash the host app. Defensive @try/@catch in the bridge layer is both a pragmatic workaround and a best practice — it protects against current and future RN framework issues.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions