Bug 270357 - "modulepreload" load failures get stuck inside the CachedResource cache
Summary: "modulepreload" load failures get stuck inside the CachedResource cache
Status: NEW
Alias: None
Product: WebKit
Classification: Unclassified
Component: Page Loading (show other bugs)
Version: Safari 17
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Nobody
URL: http://raw.justinchines.com:5000
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2024-03-01 09:41 PST by Justin Chines
Modified: 2024-03-08 09:42 PST (History)
5 users (show)

See Also:


Attachments
repro (208 bytes, text/html)
2024-03-01 09:41 PST, Justin Chines
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Justin Chines 2024-03-01 09:41:27 PST
Created attachment 470114 [details]
repro

ES Module preloading can be activated using the (relatively new) rel="modulepreload" link tags. If the network requests resulting from these tags fail (e.g. due to network connection being lost), then this failure will be cached, and the page will be stuck with a poisoned cache.
The result for typical SPAs is that essential modules will fail to load, and the page won't work at all. Refreshing does not fix the problem.

In the event of a network failure, the following is true:
- There is a CachedScript stored in the MemoryCache with:
    - isPreloaded() is true (m_preloadCount > 0)
    - errorOccurred() is true (m_status == LoadError, via SubresourceLoader::didFail)
- In CachedResourceLoader::requestResource, the RevalidationPolicy chosen for the resource is "Use", because of isPreloaded()
- At ScriptModuleLoader:471, after the script calls dynamic import(), it will fail immediately with "Importing a module script failed.", because of errorOccurred()

A subsequent request for the poisoned resource is _not_ seen in the Web Inspector network tab when the process is in this state, but the above exception message ends up being thrown in the script.

I don't know if this is sufficient information to understand the issue, but it's what I observed when debugging.
---
Here's how I've been able to reproduce:
1. Serve the repro.html file from a remote server
2. script.js can be anything, but a big file is easier. For example: `$ dd if=/dev/zero of=script.js bs=1024 count=1024`
3. Request repro.html in Safari 17
4. Cancel the load of script.js script by disabling the network connection
5. Re-enable network connection
6. The page is now stuck and will never load script.js, even after a standard refresh. Every refresh will print "Importing a module script failed." in the console. script.js _will not_ be in the network tab.

I realize these steps (especially #4) are a bit of a pain. Let me know if I can help out with something better.

We hit this organically from loading our web app over poor mobile connections. Support for `rel="modulepreload"` was added in Safari 17 from what I see online, so I suppose these are recent codepaths. I am happy to provide more information to help fix this issue if needed.
Comment 1 Justin Chines 2024-03-04 07:31:36 PST
I managed to get a hosted repro working:
http://raw.justinchines.com:5000/

Load the page and refresh.
Console will show "Importing a module script failed." Network tab will _not_ show script.js. No actual network request will be made.
Comment 2 Radar WebKit Bug Importer 2024-03-08 09:42:13 PST
<rdar://problem/124280450>