Skip to content

[blazor-wasm] A cached empty or error response for a boot resource is reused forever (force-cache); the app never boots again, even after a hard reload #69488

Open

Description

@Code-Chops

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 404 with an empty body and no Cache-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).

  1. Create and run a standalone app:
    dotnet new blazorwasm -o CacheRepro
    cd CacheRepro
    dotnet run --urls http://localhost:7301
    
  2. 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.wasm and CacheRepro.3bs6qz36rq.wasm.gz.
  3. Open http://localhost:7301/ in a fresh Chrome profile. The boot fails, which is expected. The dev server answered the missing file with 200 and an empty body.
  4. Rename both files back. curl http://localhost:7301/_framework/CacheRepro.3bs6qz36rq.wasm now returns the full file.
  5. 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.
  6. 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
  7. 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

  1. dotnet-policy-service commented on Sep 23, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @dotnet/area-extensions-caching
    See info in area-owners.md if you want to be subscribed.

  2. transferred this issue fromdotnet/runtimeon Sep 24, 2026
  3. github-actions commented on Sep 24, 2026

    @github-actions
    Contributor

    Triage Summary

    Area: area-blazor (issue concerns the Blazor WebAssembly boot loader's asset fetch/caching behavior in dotnet.js)
    Type: Bug (reproducible: the loader's force-cache fetch mode permanently reuses a stale empty/error response for a boot asset, and the reporter provides a scripted Puppeteer repro)

    Potential Duplicates

    Generated by Issue Triage Agent for dotnet/aspnetcore for #69488 · copilot · auto · 31.5 AIC · ⌖ 3.96 AIC · ⊞ 27.6K · ◷

  4. vendasankarsf3945 commented on Oct 1, 2026

    @vendasankarsf3945
    Contributor

    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

    • Created a standalone Blazor WebAssembly app targeting net10.0.
    • Identified the fingerprinted startup asset under bin/Debug/net10.0/wwwroot/_framework/ (example: CacheRepro-Net10.<hash>.wasm).
    • Temporarily made that boot asset invalid/unavailable and loaded the app once to create the failure/cache state.
    • Restored the original asset and performed both normal reload and hard reload.

    Observed:

    • The app continued to fail to boot after restore, and DevTools/Network showed continued reuse of the previously cached boot response.

    Result: Reproducible on .NET 10, consistent with the reported behavior.

    .NET 11 (net11.0.100-rc.1.26425.128) — Not Reproduced

    • Repeated validation with a standalone Blazor WebAssembly app targeting net11.0.
    • _framework startup layout differs from .NET 10 (startup resources used dotnet.<hash> / dotnet.<hash>.js; no equivalent app-specific CacheRepro11.<hash>.wasm in this setup).
    • Applied equivalent poison/restore steps on the actual startup resource.
    • Performed normal reload and hard reload after restore.

    Observed:

    • The app recovered successfully after restoring the resource.

    Result: Could not reproduce persistent cached-boot failure on .NET 11 in this environment.

    Conclusion

    • .NET 10: issue reproduced.
    • .NET 11: issue not reproduced with equivalent startup-resource validation.

    Screenshots from both runs are attached for reference.

    If this is still reproducible on the latest .NET 11 build, please share:

    • exact repro steps,
    • SDK/runtime version,
    • exact startup resource affected,
    • browser/version,
    • Network + Console logs.

    .Net 11 Version

    Image

    .Net 10 Version

    Image
  5. added this to the Backlog milestone on Oct 1, 2026
  6. oroztocil commented on Oct 2, 2026

    @oroztocil
    Member

    @vendasankarsf3945 Your .NET 11 test used dotnet.js, which loads via JavaScript import(), rather than an Wasm assembly loaded with fetch(..., { 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 uses force-cache.

    Additionally, the reason why you see a different set of files under _framework in .NET 11 is that it no longer copies most assets into bin\...\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.

  7. added
    bugThis issue describes a behavior which is not expected - a bug.
    on Oct 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

area-blazorIncludes: Blazor, Razor ComponentsbugThis issue describes a behavior which is not expected - a bug.feature-blazor-wasmThis issue is related to and / or impacts Blazor WebAssemblyinvestigate

Type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions