
Primary topic Mobile Web
Chrome 156 for Android gives mobile websites a way to recover from a frustrating failure: a feature’s JavaScript fails to download, the connection comes back, but trying again still produces an error. Google announced the stable Android release on October 20, with distribution through Google Play over the following few days. Its module-loading change can let a site restore the missing feature while keeping the rest of the page open.
The change appears in the Chrome 156 release notes as “Avoid caching module failures.” After a failed module fetch, a fresh import() can try loading the same resource again. The first failure still needs handling, and the site must initiate the next attempt. Chrome does not automatically retry a broken checkout panel, restart a component, or make an unavailable file work offline.
What is actually enabled in Chrome 156
Google’s tracker and the released code disagree. As of October 21, the ChromeStatus feature record assigns milestone 156 to desktop Chrome, Android, and Android WebView, but still labels the feature Proposed and reports it as unreleased.
The configuration for Android’s announced build, 156.0.8078.47, marks ModuleMapDoNotCacheFailedFetch as stable, without a separate Android override. Chromium’s feature configuration documentation explains that stable features are enabled by default. That supports availability in the released 156 build despite the conflicting tracker status.
Browser distribution is still gradual. A visitor using an older installed version does not gain the behavior because the release date has passed. The Android announcement also establishes no availability in Chrome on iOS. For an embedded Android WebView, verify its installed version and behavior separately rather than assuming the Chrome app’s update covers it.
Why a recovered connection could leave a feature broken
Consider a shopping page that loads its delivery-options panel only when someone opens it. The product page is already usable, but the panel needs another JavaScript module. If that download fails during a connection interruption, the site can display an error and offer another attempt. The important question is whether that attempt can actually reach the resource.
Under the previous behavior described in the feature explainer, a failed fetch left an entry in the browser’s module map, keyed by the module’s URL and type. Later imports of the same module encountered that stored failure before another fetch could begin. The failure could therefore outlast the connection problem for the lifetime of the document or worker.
Removing that failed entry changes what happens next. Once the first attempt has failed, a later import can start another fetch. In the shopping example, this gives the site a route to reopen the delivery panel without rebuilding the entire page. Preserving the shopper’s selections remains the application’s responsibility, but a full reload need not be its first recovery action.
Timing matters. The HTML standard change preserves sharing between concurrent imports: requests for the same module while its fetch is in progress still share that attempt and can fail together. Starting several imports simultaneously is not a sequence of independent retries. The useful second attempt comes after the failed one has settled.
Which failures can benefit
The relevant boundary is obtaining a usable module response. The HTML module-fetch algorithm removes the failed entry for a network or response-body failure, an unsuccessful HTTP status, or a response that cannot produce the requested module type. Chromium’s feature configuration also explicitly identifies MIME-type mismatches.
That makes a temporary connection failure a useful retry candidate. A temporary server error can also be recoverable if the server subsequently returns the file. A wrong content type can be retried after the response is corrected. In each case, removing the remembered failure creates an opportunity; the resource still has to become available in a usable form.
Syntax errors and execution errors are different. A downloaded JavaScript file with invalid syntax retains its parse error under the module-creation rules. A module that requests an export its dependency does not provide has a linking problem. Neither problem is repaired by restoring the connection and repeating the same import.
Likewise, the JavaScript module-evaluation rules retain failures that occur while the module runs. For example, a module might download correctly and then perform a top-level await fetch(...) that rejects. Although the underlying problem involves the network, it occurs during module execution. Importing that same module again does not restart its failed initialization. Applying these rules, a team should put recoverable data loading behind an explicitly repeatable operation instead of expecting another import to rerun it.
The retry button has to reach the browser
A browser-level improvement can be hidden behind an application-level cache. React’s documentation for lazy says it caches the loader’s returned Promise and does not call that loader more than once for the same lazy component. If the Promise rejects, React passes the rejection to the nearest Error Boundary.
The practical consequence is that resetting an error display alone does not establish a new loading attempt. A Retry button can render the same lazy component and encounter the same rejected Promise without invoking another native import(). Inspect the loader and the network activity together. If the loader never runs again, recovery needs attention at the framework or application layer. Creating a new lazy component on every render is not a sound shortcut: React’s troubleshooting guidance warns that this resets component state.
Also inspect the code sent to the phone. An import() in source code does not prove that the production bundle uses the browser’s native module loader. Webpack supports several chunk-loading mechanisms, including JSONP and native import, depending on its output configuration. A runtime that loads chunks through another mechanism has its own failure behavior. Test the production build and its actual retry path.
For the interface itself, a reasonable starting point is a visible error beside the failed feature, a deliberate Retry action, and a loading state while that attempt runs. Keep the already working parts of the page usable. If the application schedules retries itself, bound them and provide a way to stop waiting; this browser change supplies no retry timing or user interface.
A second request cannot restore a deleted file
Some apparent connection failures are deployment failures. Vite’s production guide describes an open page requesting an old JavaScript chunk after a new deployment has deleted it. The guide exposes vite:preloadError for application handling and shows reloading as one possible response. It does not promise automatic recovery.
If the old chunk is gone, asking for the same URL again still cannot retrieve it. Teams need a deployment policy that preserves assets required by open sessions, or a controlled transition to the new page version. An update prompt may be appropriate there. A transient connection failure calls for a different response: give the existing page a chance to finish loading its feature.
Other caches also remain relevant. Google’s service-worker caching guidance distinguishes the HTTP cache from application-managed caches and shows how a service worker can answer requests itself. Removing a module-map failure does not clear those layers or guarantee a trip to the origin server. A new load attempt still follows the site’s delivery and caching behavior.
This also sets the offline limit. If the module is unavailable both locally and over the network, repeated imports cannot supply its code. A site that needs the feature offline must arrange for the necessary resources to be available beforehand. Chrome 156’s change helps recovery when resources become obtainable again.
Test the interruption, then test the recovery
The useful test is whether the same open page can recover the same module URL after a controlled failure. Chrome’s Android remote-debugging tools let a team inspect the page on a real phone. The DevTools Request conditions panel can selectively block a resource and then remove that block, avoiding the need to make every request on the page fail.
- Start with the production build and record the installed browser version. Choose a feature whose module has not already loaded. Account for prefetching and module preloads so the first attempt genuinely needs the resource.
- Load the surrounding page, then fail the chosen module request. Block its URL or use a controlled endpoint that temporarily returns an HTTP error. Confirm that the import rejects and the page displays its recovery interface.
- Restore access without reloading the document. Keep the module URL unchanged and trigger the application’s Retry action. Reusing an old rejected Promise will not exercise the new browser behavior.
- Verify both the new load attempt and the restored feature. Inspect the request and its initiator, then complete the original interaction. Check that selections or entered information elsewhere on the page survive.
For a controlled HTTP-error test, prevent the test response from being reused by the HTTP cache, so the result isolates module-loader behavior. Then repeat with the application’s normal cache headers and service worker. Reloading the page or adding a new query string between attempts changes the conditions and does not demonstrate recovery for the same module in the same document.
Repeat the test with a failed dependency inside the lazy feature, not just its entry file. A panel can fetch its own module successfully while a helper module fails. The Web Platform Tests added for this change include repeated-import and shared-dependency cases; an application’s production loading graph deserves the same attention.
Finally, keep the failure in place and check the stopping behavior. Try a deleted chunk, malformed JavaScript, and a module that throws during initialization. These are useful controls: the page should offer an understandable next step instead of an endless spinner. Repeat relevant checks in the other browsers and embedded environments the site supports.
Judge success by the restored interaction
For a site team, the useful outcome is that someone can finish the action they started. Record the initial module-load failure, whether a fresh attempt occurred, whether the feature became usable, and whether a reload was necessary. Keep those outcomes separate: a click on Retry does not establish that a new request happened, and a successful request does not establish that the interface recovered.
Chrome 156 removes one obstacle between a temporary download failure and another attempt. The remaining work is specific: make sure the loader can run again, keep the requested asset available, and verify that the recovered feature lets the visitor continue. A phone regaining its connection should be an opportunity to finish the task already on the screen.