Blazor WebAssembly runtime intermittently fails to start under concurrent page loads (.NET 10)
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 276
Description
### Is there an existing issue for this?
- [x] I have searched the existing issues
### Describe the bug
On .NET 10, a Blazor WebAssembly app **intermittently fails to start the .NET runtime** when several
browser instances load it concurrently. The page renders its static markup, `blazor` initialises, and
then the runtime dies at startup with:
```
Error: AggregateException_ctor_DefaultMessage (Failed to fetch dynamically imported module:
http://127.0.0.1:5300/_framework/resource-collection.ty4X_.js)
TypeError: Failed to fetch dynamically imported module: .../_framework/resource-collection.ty4X_.js
at un (.../_framework/dotnet.runtime..js)
at rn.resolve_or_reject (.../_framework/dotnet.runtime..js)
...
at .../_framework/dotnet.native..wasm:wasm-function[298]
```
Nothing is rendered by any `InteractiveWebAssembly` component; the page sits on its static content.
**It is load-dependent, not random.** With one browser at a time it does not occur at all. With eight
concurrent browsers it affects roughly half of page loads.
### Expected Behavior
The runtime starts on every load. Concurrency between independent browser instances should not affect
whether an app boots.
### Steps To Reproduce
**Repro: https://github.com/Memphizzz/BlazorBootBugRepro**
```bash
git clone https://github.com/Memphizzz/BlazorBootBugRepro
cd BlazorBootBugRepro && ./repro.sh
```
Stock template, stock Chrome, no test framework and no third-party packages. The **only** addition to
the template is a component rendered with `prerender: false`, so the marker it renders can come from
nowhere but a started runtime. The repo is the output of:
```bash
dotnet new blazor -o BootRepro --interactivity WebAssembly --empty
```
`BootRepro.Client/Booted.razor`:
```razor
WASM-BOOTED
```
appended to `BootRepro/Components/Pages/Home.razor`:
```razor
```
Then run N headless Chrome instances at the same URL, each with a fresh profile, and count the DOMs
that do not contain the marker:
```bash
for i in $(seq 1 8); do
google-chrome --headless=new --disable-gpu --no-first-run \
--user-data-dir="$(mktemp -d)" --virtual-time-budget=20000 \
--dump-dom "http://127.0.0.1:5299/" > out-$i.html 2>/dev/null &
done
wait
grep -L wasm-booted out-*.html # every file listed never started the runtime
```
**Measured on the stock template above** (.NET 10.0.111, Chrome 143, Ubuntu 26.04):
| concurrency | page loads | never started the runtime |
|---|---|---|
| **1 at a time** | 8 | **0** |
| **8 at a time** | 48 | **25** |
The serial arm is the control: same app, same browser, same budget, same marker — only concurrency
differs, so this is not the dump being taken too early.
### Exceptions (if any)
```
TypeError: Failed to fetch dynamically imported module:
/_framework/resource-collection..js
```
surfaced as `AggregateException_ctor_DefaultMessage` from `dotnet.runtime..js`. It reaches
`console.error` only — it raises no `error` or `unhandledrejection` event, so a page-level error hook
sees nothing.
### .NET Version
10.0.111
### Anything else?
**ASP.NET Core**: 10.0.11 · **Chrome**: 143.0.7499.109 · **OS**: Ubuntu 26.04 (x64), WSL2 ·
no IDE (CLI).
**Additional evidence from a private harness, offered because it narrows the mechanism.** The same
failure occurs there, and a Chrome net-log (`--log-net-log`) of a failing browser shows:
- **No `URLRequest` is issued for that module on the failing load** — not an error, not a hang: no
request. The bytes never being asked for is the finding, and it is why raising client timeouts
changes nothing.
- Across **1,522** `_framework` requests in that browser: **zero** ended with a `net_error` and
**zero** never completed.
- When the module *is* requested it is served correctly — `200`, `Content-Type: application/javascript`,
gzip, full length — so this is not the server, the MIME type, or the fingerprint.
- A per-load table across three non-victim browsers: **22 of 22** first-of-test loads fetched the
module, no exceptions. A failing load is the one that did not ask for it.
**Note on the resource**: `resource-collection.{fingerprint}.js` does not exist on disk; it is
generated and served by middleware (see #62249). We have not established whether that is relevant.
**Why this repo**: the failing import is of a Blazor asset produced by the ASP.NET Core static-asset /
import-map machinery, and the repro is a Blazor template — so `aspnetcore`. The stack, however, is
inside `dotnet.runtime.js` and `dotnet.native.wasm`, so if the defect is in the runtime's startup
import path this belongs in `dotnet/runtime` and we would expect a transfer rather than a close.
Contributor guide
Research direction
Clone Memphizzz/BlazorBootBugRepro and run repro.sh, then inspect the generated resource-collection import path in dotnet.runtime.js, dotnet.native.wasm, and the ASP.NET Core static-asset/import-map machinery described in #62249. Confirm the missing request under concurrent loads and determine whether the defect is in runtime startup or asset serving; done means every concurrent browser starts the runtime and renders the wasm-booted marker.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, wasm
- Domain
- backend, frontend, web-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 42/100