From 9ad6d0974dd5c4cb590d28b70771eed0b04c7069 Mon Sep 17 00:00:00 2001 From: yunsung Date: Fri, 28 Aug 2026 11:03:32 +0900 Subject: [PATCH 1/2] fix(ios): keep the original error message for script download failures NSURLError leaves localizedFailureReason nil, so the reject block received a nil message and React Native replaced it with "Unknown error from a native module". Every transport-level failure therefore looked identical in production logs. Fall back to localizedDescription, append the error domain and code, and pass the NSError through so its userInfo survives. The reject code stays ScriptDownloadFailure, so the retry gate in loadScriptWithRetry is unaffected. --- packages/repack/ios/ScriptManager.mm | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/repack/ios/ScriptManager.mm b/packages/repack/ios/ScriptManager.mm index 0a2909f6c..1dc10e696 100644 --- a/packages/repack/ios/ScriptManager.mm +++ b/packages/repack/ios/ScriptManager.mm @@ -26,6 +26,20 @@ @interface RCTBridge (RCTTurboModule) static NSURLSession * (^_urlSessionFactory)(void) = nil; static NSURLSession *_cachedURLSession = nil; +// NSURLError instances fill in localizedDescription but leave localizedFailureReason nil. +// Passing a nil message to the reject block makes React Native substitute +// "Unknown error from a native module", which hides whether the download timed out, +// lost the connection or failed TLS. Fall back to the description and append the +// domain/code so the cause survives the bridge. +static NSString *DescribeScriptDownloadError(NSError *error) +{ + NSString *reason = error.localizedFailureReason ?: error.localizedDescription; + if (reason.length == 0) { + reason = @"Script download failed"; + } + return [NSString stringWithFormat:@"%@ (%@ %ld)", reason, error.domain, (long)error.code]; +} + @implementation ScriptManager RCT_EXPORT_MODULE() @@ -88,7 +102,7 @@ - (NSURLSession *)urlSession [self downloadAndCache:config completionHandler:^(NSError *error) { if (error) { - reject(ScriptDownloadFailure, error.localizedFailureReason, nil); + reject(ScriptDownloadFailure, DescribeScriptDownloadError(error), error); } else { [self execute:config resolve:resolve reject:reject]; } @@ -139,7 +153,7 @@ - (NSURLSession *)urlSession [self downloadAndCache:config completionHandler:^(NSError *error) { if (error) { - reject(ScriptDownloadFailure, error.localizedFailureReason, nil); + reject(ScriptDownloadFailure, DescribeScriptDownloadError(error), error); } else { resolve(nil); } From 2d8fb1ecd99172f11aa3de6ac36c88ee9fd592da Mon Sep 17 00:00:00 2001 From: yunsung Date: Fri, 28 Aug 2026 11:03:57 +0900 Subject: [PATCH 2/2] chore: add changeset for iOS script download error message --- .changeset/ios-script-download-error-message.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/ios-script-download-error-message.md diff --git a/.changeset/ios-script-download-error-message.md b/.changeset/ios-script-download-error-message.md new file mode 100644 index 000000000..bc9c4cff3 --- /dev/null +++ b/.changeset/ios-script-download-error-message.md @@ -0,0 +1,5 @@ +--- +"@callstack/repack": patch +--- + +Keep the original error message for iOS script download failures instead of surfacing "Unknown error from a native module".