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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions