GeezerVison is showing github.com — fix(runtime): give debug HTTP module fetches a 30s request budget by aleclarson · Pull Request #552 · NativeScript/ios
Conversation
aleclarson
commented
Nov 10, 2026
•
edited by coderabbitai
Bot
Loading
Uh oh!
There was an error while loading. Please reload this page.
Summary
Every HTTP module request carried a flat 5-second NSURLRequest timeout. Dev servers transform cold modules on demand, and real toolchain responses have been measured at 5–9s — so valid fetches expired mid-transform. A timed-out dependency fetch left the module unregistered, its subtree stayed undiscovered by the graph walk, and instantiation fell back to the blocking synchronous loader (the module graph walk missed warnings), in the worst cases stalling the JS thread for minutes.
- One timeout policy, one place.
ModuleFetchTimeoutInterval() reads RuntimeConfig.IsDebug: 30s in debug runtimes, the original 5s in release. It now backs the synchronous NSURLConnection fetch, every async NSURLSession request, and the async session's own default — whose stale 10s never actually applied, since each request overrode it to 5s.
- Retries bounded by the graph budget. Two 30s attempts plus the retry delay can outlast the 60s pumped graph wait, so the enclosing load's deadline (
AsyncGraphLoad.fetchDeadline) is plumbed into each async attempt: requests clamp timeoutInterval to the time remaining, the 120ms transport retry fires only while budget is left, and no request is issued that could only complete past the deadline. The sync-fallback path — the post-deadline recovery mechanism — keeps its existing single retry.
- Unpumped loads (dynamic
import() of an HTTP root) pass a zero deadline and keep unbounded fetches; nothing there waits on the 60s budget.
Diagnostics are unchanged: the graph-miss warning still fires, and 404 / MIME / syntax / transport failures keep their existing messages.
Test plan
- New
ModuleTestServer routes: delayed /esm/slow/* transform routes and a /esm/request-stats.json introspection route so specs can prove a module was fetched exactly once (no retry, no sync-fallback round trip).
- New specs in
HttpEsmLoaderTests (build-aware via the debug-only canonicalizeHttpUrlKey export):
- a 7s-delayed module resolves inside the debug budget on its first request (rejects in release);
- a slow HTTP parent re-exporting a child resolves with one request per module;
- a pumped
require() through a local→HTTP re-export boundary waits for a 7s leaf.
- Existing
network timeouts spec updated to wait out the debug budget (delayMs 32000 vs the former 12000).
- Full TestRunner suite on CI.
Local validation status: both modified translation units pass clang -fsyntax-only; build-for-testing of the TestRunner scheme succeeds. A local on-device run could not validate further in this environment: the local Xcode is newer than CI's pinned toolchain, so the app needed local-only build overrides, and the resulting TestRunner app crashes at startup identically on pristine main (uncaught exception in RunMainScript) — a host-environment issue that predates this change. The timeout policy itself was additionally verified earlier against the published runtime via an effective-request-override experiment on a real dev session (delayed 5–9s responses stopped timing out; graph misses disappeared).
Summary by CodeRabbit
- Bug Fixes
- HTTP module graph loads now share a time budget, preventing individual requests from exceeding the remaining time and skipping retries when there isn’t enough time left.
- Request timeouts are 5 seconds in release builds and 30 seconds in debug builds.
- Pumped
require() calls use the same time budget for waiting and fetching modules.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Every module request carried a flat 5s timeout. Dev servers transform
cold modules on demand and real responses have been measured at 5-9s, so
valid fetches expired mid-transform: the async walk left the dependency
unregistered, its subtree stayed undiscovered, and instantiation fell
back to the blocking synchronous loader ("module graph walk missed").
The timeout is now selected once, from RuntimeConfig.IsDebug, and shared
by the synchronous fetch, each async attempt, and the async session
default (whose stale 10s never applied to requests anyway): 30s in debug
runtimes, the original 5s in release.
To keep fetch behavior coherent with the pumped wait, the enclosing
graph load's deadline now bounds each async attempt — request timeouts
clamp to the time remaining and the transport-error retry fires only
while budget is left, so a fetch can never outlive the 60s wait pumping
for it. Unpumped (dynamic-import) loads keep unbounded fetches.
Regression coverage: new ModuleTestServer slow-transform routes plus a
request-count introspection route let specs prove a delayed module
resolves on a single request — a direct import(), an HTTP re-export
chain, and a pumped require() through a local->HTTP boundary. The
network-timeout spec now waits out the debug budget.
coderabbitai
Bot
commented
Nov 10, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
|

Walkthrough
-
Module graph loads now pass an optional absolute fetch deadline to HTTP module requests. Attempts apply build-dependent request timeouts, clamp them to the remaining budget, and limit transport retries. Tests cover delayed responses, re-export chains, pumped require() calls, and request counts.
Changes: Module fetch budget
| Layer / File(s) |
Summary |
Graph deadline propagation NativeScript/runtime/HttpLoader.h, NativeScript/runtime/ModuleInternalCallbacks.* |
StartModuleGraphLoad and FetchModuleBodyAsync accept a fetch deadline. Pumped graph loads pass their wait deadline; dynamic HTTP imports pass 0, which means unbounded. |
HTTP attempt budgeting NativeScript/runtime/HttpLoader.mm |
Module requests use a 30-second timeout in debug runtimes and a 5-second timeout otherwise. Async attempts cap request timeouts to the remaining budget and skip transport retries when the delay does not fit. |
Delayed-response coverage TestRunner/app/tests/HttpEsmLoaderTests.js, TestRunner/app/tests/esm/mixed/*, TestRunnerTests/TestRunnerTests.swift |
Tests cover delayed direct imports, re-export chains, and pumped require() calls. The test server serves delayed module responses and reports request counts. |
Estimated code review effort: 3 (Moderate) | ~20 minutes
Change: Bug fix
Sequence Diagram(s)
sequenceDiagram
participant PumpedRequire as Pumped require()
participant GraphLoader as Module graph loader
participant HttpLoader as HTTP module loader
participant TestServer as Module test server
PumpedRequire->>GraphLoader: Start graph load with wait deadline
GraphLoader->>HttpLoader: Fetch module with graph deadline
HttpLoader->>TestServer: Send request with remaining-time timeout
TestServer-->>HttpLoader: Return module response
HttpLoader-->>GraphLoader: Complete module fetch
GraphLoader-->>PumpedRequire: Complete graph load
Loading
Suggested reviewers: nathanwalker
Merge Risk: Minimal · up to 412c1
-
This change raises the HTTP module fetch timeout to 30 seconds in debug runtimes and bounds pumped graph loads by their wait deadline. No concrete merge-blocking risk remains in the supplied evidence. The full test suite has not run on CI, so that normal check should pass before merging.
Pre-merge checks: 4 · 1
-
Failed checks: 1 warning
| Check name |
Status |
Explanation |
Resolution |
| Docstring Coverage |
 |
Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… |
Write docstrings for the functions missing them to satisfy the coverage threshold. |
Passed checks: 4 passed
-
| Check name |
Status |
Explanation |
| Description Check |
 |
Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check |
 |
The title clearly and concisely describes the main change: increasing the debug HTTP module fetch request budget to 30 seconds. |
| Linked Issues check |
 |
Check skipped because no linked issues were found for this pull request. |
| Out of Scope Changes check |
 |
Check skipped because no linked issues were found for this pull request. |
Full details: Docstring Coverage
-
Explanation
Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 3 functions across 6 files.
Resolution
Write docstrings for the functions missing them to satisfy the coverage threshold.
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Share
-
A rabbit checks the clock at dawn The fetch begins before the lawn Each request fits its budgeted span Retries wait only when they can Slow leaves arrive, then graphs are done The rabbit hops beneath the sun
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
|
@aleclarson please address the code rabbit comment and this should be good to go
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
The pumped require() spec imports /esm/slow/req-leaf.mjs, which the
route condition missed and answered 404 text/plain. Prefix-match the
slow routes so every /esm/slow/* path gets the delayed module body.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
This branch has not been deployed
No deployments
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Milestone
No milestone
Development
Successfully merging this pull request may close these issues.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
|