## Environment - `@callstack/repack` 5.2.5 (also verified on `@callstack/repack@5.3.0` and `main`) - React Native 0.83.9, iOS 26.x, new architecture ## Problem On iOS, every transport-level script download failure reaches JavaScript as the generic message `Unknown error from a native module`. The underlying `NSError` description is dropped, so a timeout, a dropped connection, a TLS failure and a DNS failure are indistinguishable in production logs. The cause is in `packages/repack/ios/ScriptManager.mm` — two call sites, in `loadScript` and `prefetchScript`: ```objc [self downloadAndCache:config completionHandler:^(NSError *error) { if (error) { reject(ScriptDownloadFailure, error.localizedFailureReason, nil); } else { ``` `NSURLError` populates `localizedDescription`, but leaves `localizedFailureReason` `nil`. When the message passed to `RCTPromiseRejectBlock` is `nil`, React Native substitutes its own fallback string, so the original reason never reaches JS. HTTP status failures are unaffected because `downloadAndCache` builds those errors itself and sets `NSLocalizedFailureReasonErrorKey` explicitly: ```objc NSDictionary *userInfo = @{ NSLocalizedFailureReasonErrorKey : [NSString stringWithFormat:@"Request should have returned with 200 HTTP status, but instead it received %ld", (long)statusCode] }; ``` That asymmetry is what makes the bug easy to miss: `403` failures log a useful message, while every network failure logs the generic one. Android is not affected — `RemoteScriptLoader.kt` falls back with `e.message ?: e.toString()`. ## Reproduction 1. Serve a remote container or chunk over HTTPS. 2. Load it on a device with a degraded connection, or with Network Link Conditioner set to a profile slow enough to exceed the script `timeout` (default 30s). 3. Observe the rejection reaching JS. Expected: a message identifying the failure, e.g. `The request timed out. (NSURLErrorDomain -1001)` Actual: `Unknown error from a native module` ## Impact We hit this while investigating production reports of the app not opening. Datadog RUM showed 762 transport-level CDN failures over 7 days, and on iOS every one of them carried only the generic message, so we could not tell whether raising the timeout would help or whether we were looking at TLS or DNS problems instead. ## Suggested fix Fall back to `localizedDescription` and include the error domain and code, and pass the `NSError` through as the third argument so `userInfo` survives: ```objc 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]; } ``` ```objc reject(ScriptDownloadFailure, DescribeScriptDownloadError(error), error); ``` The reject code stays `ScriptDownloadFailure`, so the retry gate in `loadScriptWithRetry` (`LOADING_ERROR_CODES`) keeps working unchanged. Happy to open a PR with this if the approach looks right.