Repository navigation
Description
Description
The boot manifest marks fingerprinted boot resources with "cache": "force-cache", and the loader passes that mode straight to fetch:
// dotnet.js (10.0.12), minified
e.cache ? o.cache = e.cache : Pe.config.disableNoCacheFetch || (o.cache = "no-cache")force-cache returns any stored response without revalidating it. If the browser ever stored an unusable response for one of these URLs (an empty body or a 404), every later boot reuses it. The integrity check fails, the loader's retries hit the same cache entry, and the app fails to start with:
Failed to find a valid digest in the 'integrity' attribute for resource '.../_framework/CacheRepro.3bs6qz36rq.wasm' with computed SHA-256 integrity '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='. The resource has been blocked.
Error in mono_download_assets: Error: download '.../_framework/CacheRepro.3bs6qz36rq.wasm' for CacheRepro.3bs6qz36rq.wasm failed 0 TypeError: Failed to fetch
(47DEQpj8... is the SHA-256 of an empty body.)
The server serves the correct file again at that point, but the app never asks for it. A hard reload does not help, because an explicit fetch cache mode overrides the reload's cache bypass. The user has to clear the site data by hand. Nothing in the error points them there.
How a bad entry gets stored
The browser needs to see a missing file once. For fingerprinted URLs that happens whenever the boot manifest and the files briefly disagree:
- Development: the page loads while the app is rebuilding. The new manifest names new fingerprints before the server can serve them. We hit this with a Blazor Web App run from the IDE: the server answered
404with an empty body and noCache-Control, and Chrome stored 17 of those responses. - Production: during a rolling deploy, an instance with the new version serves the manifest, and a resource request lands on an instance that still has the old files. The same applies to a CDN that briefly lacks the new files.
In the standalone dev server (Microsoft.AspNetCore.Components.WebAssembly.DevServer), a missing file is served as 200 with Content-Length: 0, and Chrome stores that too.
Reproduction Steps
Environment: .NET SDK 10.0.401, runtime and WebAssembly packages 10.0.12, Chrome 153.0.8010.53, macOS 26.6.2 (arm64).
- Create and run a standalone app:
dotnet new blazorwasm -o CacheRepro cd CacheRepro dotnet run --urls http://localhost:7301 - Simulate a file the server briefly cannot serve. Rename the app assembly and its compressed copy in
bin/Debug/net10.0/wwwroot/_framework/, e.g.CacheRepro.3bs6qz36rq.wasmandCacheRepro.3bs6qz36rq.wasm.gz. - Open
http://localhost:7301/in a fresh Chrome profile. The boot fails, which is expected. The dev server answered the missing file with200and an empty body. - Rename both files back.
curl http://localhost:7301/_framework/CacheRepro.3bs6qz36rq.wasmnow returns the full file. - Reload the page, then hard-reload it (Cmd+Shift+R). The boot still fails with the same integrity error. DevTools shows the request served from the disk cache.
- In the console:
const r = await fetch('_framework/CacheRepro.3bs6qz36rq.wasm', { cache: 'only-if-cached', mode: 'same-origin' }); r.status, (await r.arrayBuffer()).byteLength // 200, 0
- Overwrite the entry and reload. The app boots:
await (await fetch('_framework/CacheRepro.3bs6qz36rq.wasm', { cache: 'reload' })).arrayBuffer(); location.reload();
We scripted steps 2 to 7 with Puppeteer and got the same result on every run.
Expected behavior
A transient problem on the server should not permanently break the app in that browser. When a resource fetched with force-cache fails, the loader should fetch it once more with cache: "reload" before giving up. A failure here means an integrity mismatch, a non-OK status or an empty body. That keeps the benefit of force-cache in the normal case, because the cache is only bypassed after it returned something unusable.
Actual behavior
The loader's retries reuse the same cached entry, so the boot fails on every page load until the user clears the site data.
Regression?
No response
Known Workarounds
No response
Configuration
No response
Other information
No response
Activity
dotnet-policy-service commentedon Sep 23, 2026
Tagging subscribers to this area: @dotnet/area-extensions-caching
See info in area-owners.md if you want to be subscribed.
github-actions commentedon Sep 24, 2026
Triage Summary
Area:
area-blazor(issue concerns the Blazor WebAssembly boot loader's asset fetch/caching behavior indotnet.js)Type:
Bug(reproducible: the loader'sforce-cachefetch mode permanently reuses a stale empty/error response for a boot asset, and the reporter provides a scripted Puppeteer repro)Potential Duplicates
vendasankarsf3945 commentedon Oct 1, 2026
Hi @Code-Chops,
Thanks for the report. I validated this issue locally on both .NET 10 and .NET 11.
Blazor WebAssembly Boot Resource Caching Behavior in .NET 10 and .NET 11
.NET 10 (net10.0) — Reproduced
bin/Debug/net10.0/wwwroot/_framework/(example:CacheRepro-Net10.<hash>.wasm).Observed:
Result: Reproducible on .NET 10, consistent with the reported behavior.
.NET 11 (net11.0.100-rc.1.26425.128) — Not Reproduced
_frameworkstartup layout differs from .NET 10 (startup resources useddotnet.<hash>/dotnet.<hash>.js; no equivalent app-specificCacheRepro11.<hash>.wasmin this setup).Observed:
Result: Could not reproduce persistent cached-boot failure on .NET 11 in this environment.
Conclusion
Screenshots from both runs are attached for reference.
If this is still reproducible on the latest .NET 11 build, please share:
.Net 11 Version
.Net 10 Version
oroztocil commentedon Oct 2, 2026
@vendasankarsf3945 Your .NET 11 test used
dotnet.js, which loads via JavaScriptimport(), rather than an Wasm assembly loaded withfetch(..., { cache: "force-cache" }). These use different caching paths, which likely explains the different result. Recovery in that test doesn’t establish that the issue is fixed - the .NET 11 assembly loader still usesforce-cache.Additionally, the reason why you see a different set of files under
_frameworkin .NET 11 is that it no longer copies most assets intobin\...\wwwroot\_framework. Instead they’re served from locations recorded in the static-web-assets manifests. An equivalent repro needs to target an actual assembly request and its backing file, including any compressed representation.