Skip to content

Harden on-demand JS segment load path against missing cache file - #57855

Closed
javache wants to merge 1 commit into
react:mainfrom
javache:export-D114761643
Closed

Harden on-demand JS segment load path against missing cache file#57855
javache wants to merge 1 commit into
react:mainfrom
javache:export-D114761643

Conversation

@javache

@javache javache commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary:
Harden the on-demand (lazily code-split) JS segment load path against a missing cache file.

When RN lazily loads an on-demand JS segment, the file lives in an OS-purgeable / LRU-evictable on-demand cache and can already be gone by the time the segment is registered. On a missing file JSBigFileString::fromPath throws std::runtime_error("... Could not open file: ..."), and ReactInstance::registerSegment had no failure handling, so the throw surfaced as an unhandled JS exception and crashed the app.

Fix (V4, addresses pieterdb's latest review feedback):

  • JSBigFileString::fromPath's throwing contract is now documented in the header: it throws std::runtime_error when the file cannot be opened or read, and callers loading files that may legitimately be gone (an OS-purgeable / LRU-evictable cache) must catch and degrade gracefully. No behavior change to fromPath and no new factory.
  • ReactInstance::registerSegment builds the segment buffer on the calling thread (outside the scheduleWork callback) inside a try/catch. On the thrown exception it logs an ERROR and returns early, so a missing segment never schedules evaluation and the throw no longer escapes. The buffer is carried into the callback as a std::shared_ptr<const JSBigFileString> (RuntimeScheduler callbacks are copyable std::functions; evaluateJavaScript takes const shared_ptr<const Buffer>&). This keeps file I/O off the JS thread.
  • Dropped the inline access(R_OK) pre-check: the open() failure inside fromPath is the single source of truth, removing the redundant check and its TOCTOU window.

Changelog:
[iOS][Fixed] - Avoid an unhandled exception in bridgeless ReactInstance::registerSegment when an on-demand JS segment file is missing from the cache at lazy-load time

Reviewed By: zeyap, javache

Differential Revision: D114761643

Summary:
Harden the on-demand (lazily code-split) JS segment load path against a missing cache file.

When RN lazily loads an on-demand JS segment, the file lives in an OS-purgeable / LRU-evictable on-demand cache and can already be gone by the time the segment is registered. On a missing file `JSBigFileString::fromPath` throws `std::runtime_error("... Could not open file: ...")`, and `ReactInstance::registerSegment` had no failure handling, so the throw surfaced as an unhandled JS exception and crashed the app.

Fix (V4, addresses pieterdb's latest review feedback):
- `JSBigFileString::fromPath`'s throwing contract is now documented in the header: it throws `std::runtime_error` when the file cannot be opened or read, and callers loading files that may legitimately be gone (an OS-purgeable / LRU-evictable cache) must catch and degrade gracefully. No behavior change to `fromPath` and no new factory.
- `ReactInstance::registerSegment` builds the segment buffer on the calling thread (outside the `scheduleWork` callback) inside a try/catch. On the thrown exception it logs an ERROR and returns early, so a missing segment never schedules evaluation and the throw no longer escapes. The buffer is carried into the callback as a `std::shared_ptr<const JSBigFileString>` (`RuntimeScheduler` callbacks are copyable `std::function`s; `evaluateJavaScript` takes `const shared_ptr<const Buffer>&`). This keeps file I/O off the JS thread.
- Dropped the inline `access(R_OK)` pre-check: the `open()` failure inside `fromPath` is the single source of truth, removing the redundant check and its TOCTOU window.

Changelog:
[iOS][Fixed] - Avoid an unhandled exception in bridgeless `ReactInstance::registerSegment` when an on-demand JS segment file is missing from the cache at lazy-load time

Reviewed By: zeyap, javache

Differential Revision: D114761643
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Aug 7, 2026
@facebook-github-tools facebook-github-tools Bot added p: Facebook Partner: Facebook Partner labels Aug 7, 2026
@meta-codesync

meta-codesync Bot commented Aug 7, 2026

Copy link
Copy Markdown

@javache has exported this pull request. If you are a Meta employee, you can view the originating Diff in D114761643.

@meta-codesync meta-codesync Bot closed this in fff4994 Aug 7, 2026
@meta-codesync meta-codesync Bot added the Merged This PR has been merged. label Aug 7, 2026
@meta-codesync

meta-codesync Bot commented Aug 7, 2026

Copy link
Copy Markdown

This pull request has been merged in fff4994.

@javache
javache deleted the export-D114761643 branch August 7, 2026 16:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged. meta-exported p: Facebook Partner: Facebook Partner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant