From 652881ecddbefb9a81775b0de44afa79d7d26bd2 Mon Sep 17 00:00:00 2001 From: Vitali Zaidman Date: Thu, 26 Feb 2026 03:37:55 -0800 Subject: [PATCH] fix RCTBundleURLProvider crash when failing to check isPackagerRunning (#55761) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/55761 In certain situations, where there are network issues reaching the Dev Server, the app crashes because of waiting too long on a semaphore. Instead, reduce the timeout on the `/status` request to 6 seconds, which is plenty of time to validate that the Dev Server is running, and reduce the timeout for the semaphore to 8 seconds, in case the request still won't finish by then. Changelog: [iOS][Fixed] not crashing on network issues with connecting to DevServer Reviewed By: cipolleschi Differential Revision: D94382277 --- .../React/Base/RCTBundleURLProvider.mm | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/packages/react-native/React/Base/RCTBundleURLProvider.mm b/packages/react-native/React/Base/RCTBundleURLProvider.mm index b599cb95eca7..9032d5d5dff3 100644 --- a/packages/react-native/React/Base/RCTBundleURLProvider.mm +++ b/packages/react-native/React/Base/RCTBundleURLProvider.mm @@ -31,6 +31,8 @@ void RCTBundleURLProviderAllowPackagerServerAccess(BOOL allowed) static NSString *const kRCTEnableDevKey = @"RCT_enableDev"; static NSString *const kRCTEnableMinificationKey = @"RCT_enableMinification"; static NSString *const kRCTInlineSourceMapKey = @"RCT_inlineSourceMap"; +static const NSTimeInterval kRCTPackagerStatusRequestTimeout = 6; +static const NSTimeInterval kRCTPackagerStatusRequestTimeoutGraceTime = 2; @implementation RCTBundleURLProvider @@ -96,22 +98,39 @@ + (BOOL)isPackagerRunning:(NSString *)hostPort scheme:(NSString *)scheme NSURLSession *session = [NSURLSession sharedSession]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy - timeoutInterval:10]; + timeoutInterval:kRCTPackagerStatusRequestTimeout]; [[RCTDevSupportHttpHeaders sharedInstance] applyHeadersToRequest:request]; __block NSURLResponse *response; __block NSData *data; + __block BOOL isRunning = NO; dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); [[session dataTaskWithRequest:request - completionHandler:^(NSData *d, NSURLResponse *res, __unused NSError *err) { + completionHandler:^(NSData *d, NSURLResponse *res, NSError *err) { data = d; response = res; + NSString *status = [[NSString alloc] initWithData:d encoding:NSUTF8StringEncoding]; + isRunning = [status isEqualToString:@"packager-status:running"]; + if (!isRunning) { + RCTLogWarn( + @"Packager status check returned unexpected result for %@: %@, error: %@", url, status, err); + } dispatch_semaphore_signal(semaphore); }] resume]; - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); - - NSString *status = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - return [status isEqualToString:@"packager-status:running"]; + long result = dispatch_semaphore_wait( + semaphore, + dispatch_time( + DISPATCH_TIME_NOW, + (int64_t)((kRCTPackagerStatusRequestTimeout + + // The request timeout does not cover all phases of the request (e.g. DNS resolution), + // so the actual request might take slightly longer than the configured timeout. + kRCTPackagerStatusRequestTimeoutGraceTime) * + NSEC_PER_SEC))); + if (result != 0) { + RCTLogWarn(@"Packager status check timed out for %@", url); + return NO; + } + return isRunning; } - (NSString *)guessPackagerHost