From 9f41b14761389b57f4947e89051a7865122ba4c6 Mon Sep 17 00:00:00 2001 From: Luxlorys Date: Mon, 2 Feb 2026 23:26:12 +0200 Subject: [PATCH] Fix crash in async void TurboModule methods when NSException is thrown When an async void TurboModule method throws an NSException (e.g., during app termination or rapid UI transitions), the exception is caught and converted to a JSError which is then rethrown. However, since this code executes inside an async dispatch block via invokeAsync(), nothing in the call stack can catch the C++ exception, causing immediate app termination with SIGABRT/EXC_BAD_ACCESS. This is particularly common during: - Rapid screen transitions while closing the app - Focusing an input (triggering keyboard) then quickly closing the app - Any scenario where native modules are deallocated while async void methods are still executing The fix replaces the throw with RCTLogError, which preserves error visibility for debugging while preventing the crash. This is safe because: - Void methods have no return value to the caller - Async dispatch means the original JS call already returned - There's no Promise to reject This is consistent with the existing handling in performMethodInvocation, which already has special handling for async exceptions (it re-throws NSException instead of converting to JSError for async calls). Co-Authored-By: Claude Opus 4.5 --- .../core/platform/ios/ReactCommon/RCTTurboModule.mm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm index b36c819cca85..47bb1d1ea65f 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm @@ -461,7 +461,13 @@ TraceSection s( @try { [inv invokeWithTarget:strongModule]; } @catch (NSException *exception) { - throw convertNSExceptionToJSError(runtime, exception, std::string{moduleName}, methodNameStr); + // Cannot rethrow C++ exceptions from async dispatch - nothing can catch them. + // Log the error for debugging purposes instead of crashing. + RCTLogError( + @"Exception thrown while invoking async method %s.%s: %@", + moduleName, + methodNameStr.c_str(), + exception); } @finally { [retainedObjectsForInvocation removeAllObjects]; }