Conversation
|
Hi @LoraBaek! Thank you for your pull request and welcome to our community. Action RequiredIn order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you. ProcessIn order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA. Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks! |
|
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks! |
|
@fabriziocucci has imported this pull request. If you are a Meta employee, you can view this in D122990077. |
Summary:
When the native networking layer emits
didCompleteNetworkResponsewith a falsy error but nodidReceiveNetworkResponsewas ever delivered for that request,XMLHttpRequest.__didCompleteResponsetreats the request as successful and dispatchesloadwhilestatusis still0.whatwg-fetch(which backs the globalfetchin React Native) then runsnew Response(body, {status: 0})inside asetTimeoutinxhr.onload. TheResponseconstructor throwsRangeError: Failed to construct 'Response': The status provided (0) is outside the range [200, 599]. Because the throw happens outside the promise chain, thefetch()promise never settles, nocatchcan observe it, and in a release build the uncaught error terminates the app.We hit this in production (Android 16, Hermes, RN 0.83.10) on a plain HTTPS GET. Sentry breadcrumbs show the XHR finishing with
status_code: 0, immediately followed by the fatalRangeError(mechanismonerror,handled: no). The device reported itself online over Wi-Fi.A completed request that never received a response is a failure, not a successful load. Browsers never fire
loadwithstatus === 0. This change makes__didCompleteResponsetreat!error && status === 0as an error, soonerrorfires andfetch()rejects with the usualTypeError: Network request failed, which callers already handle.This also hardens the iOS path:
RCTNetworking.mmsendsRCTNullIfNil(error.localizedDescription), so an error without a description currently reaches JS as a "success" as well.Status
0is safe as the signal: HTTP has no status 0, AndroidNetworkingModulecustom URI handlers report 200, andRCTNetworking.mmreports 200 for non-HTTPNSURLResponses.Related: #38625 (same
RangeErrorviafile://, closed for missing repro). Thefile://case was later special-cased in whatwg-fetch, but any other status-0 completion still crashes.Changelog:
[GENERAL] [FIXED] - XMLHttpRequest dispatches
errorinstead ofloadwhen a request completes without ever receiving a response (status 0), preventing an uncatchableRangeErrorinfetch()Test Plan:
__didCompleteResponse(requestId, null)without a prior__didReceiveResponsedispatcheserrorandloadend, notload.load.loadis dispatched withstatus === 0); with it, the suite passes.yarn jest packages/react-native/Libraries/Network/__tests__/XMLHttpRequest-test.jsyarn flow: No errors.eslint --max-warnings 0andprettier --checkon both files pass.