Skip to content

[release/10.0] [mono][wasm] Fix AOT + BlazorWebAssemblyLazyLoad startup crash#131038

Open
lewing wants to merge 1 commit into
dotnet:release/10.0from
lewing:lewing-backport-131020-release-10-0
Open

[release/10.0] [mono][wasm] Fix AOT + BlazorWebAssemblyLazyLoad startup crash#131038
lewing wants to merge 1 commit into
dotnet:release/10.0from
lewing:lewing-backport-131020-release-10-0

Conversation

@lewing

@lewing lewing commented Jul 19, 2026

Copy link
Copy Markdown
Member

Backport of #131020 to release/10.0

/cc @lewing

  1. Fixes Issue [wasm][AOT] Assertion at appdomain.c:188 — g_assert(string_empty_fld) fails on any AOT-compiled Blazor WASM project (.NET 10) #125794

main PR: #131020

Description

AOT-published Blazor WebAssembly apps that use BlazorWebAssemblyLazyLoad crash during Mono runtime startup — reported as an appdomain.c assertion (condition '<disabled>' not met) and, in other configurations, an interp.c NIY … should not be reached abort.

An AOT image hard-binds to every assembly it references when the image is loaded (load_aot_module in aot-runtime.c eagerly calls load_image for all referenced images unless the aot-lazy-assembly-load runtime option is set). A BlazorWebAssemblyLazyLoad assembly is not present at runtime startup (it is downloaded on demand), so when the main app's AOT image loads, resolving the lazy dependency fails, the image is marked unusable, and every method in it falls back to the interpreter, which then hits an unsupported construct and aborts.

The fix automatically enables the runtime's existing aot-lazy-assembly-load option from the WebAssembly SDK when AOT is combined with BlazorWebAssemblyLazyLoad. This defers binding of a referenced lazy assembly until it is actually loaded, keeping the referencing AOT image usable. The change is scoped to RunAOTCompilation=true + at least one BlazorWebAssemblyLazyLoad item, so non-AOT and non-lazy builds are unaffected.

Customer Impact

Any Blazor WebAssembly app that combines AOT compilation with BlazorWebAssemblyLazyLoad crashes on startup and is completely unusable. Lazy loading is a common technique to reduce initial download size, and AOT is a common way to improve runtime performance, so customers who adopt both features together are blocked with no workaround other than disabling one of them.

Regression

No. This is a long-standing interaction between AOT and lazy loading, not a regression introduced in the most recent release.

Testing

Added LoadLazyAssemblyWithAOT to LazyLoadingTests (native-mono, Release + AOT + lazy load), which previously only covered the interpreter (Debug) configuration. The new test reproduces the crash without the fix and passes with it.

Verified locally by building the browser (Mono + CoreCLR runtime packs) and workload and running the new test:

  • Without the fix: module WasmBasicTestApp is unusable because dependency Json is not foundinterp.c:4135 assertion → timed out waiting for WASM EXIT.
  • With the fix: AOT: image 'WasmBasicTestApp' found., firstJsonLoad=true, {"FirstName":"John","LastName":"Doe"}, WASM EXIT 0.

Risk

Low. The change is a single, narrowly-scoped MSBuild property that only appends --aot-lazy-assembly-load when RunAOTCompilation=true and at least one BlazorWebAssemblyLazyLoad item is present. Builds that do not combine AOT with lazy loading are unaffected, and the option simply defers assembly binding that would otherwise fail.

Note

This backport pull request was authored with GitHub Copilot.

When an app is published with AOT and uses BlazorWebAssemblyLazyLoad, the
Mono runtime crashes during startup (appdomain.c assertion / interp.c NIY
'should not be reached').

Root cause: an AOT image hard-binds to every assembly it references when the
image is loaded (see load_aot_module in aot-runtime.c, which eagerly calls
load_image for all referenced images unless aot-lazy-assembly-load is set). A
lazy-loaded assembly is not present at runtime startup, so any AOT image that
references it is marked 'unusable because dependency <asm> is not found' and its
methods fall back to the interpreter, which then hits an unsupported construct
and aborts.

Enable the runtime's 'aot-lazy-assembly-load' option automatically from the
WebAssembly SDK when AOT is combined with BlazorWebAssemblyLazyLoad, so
referenced lazy assemblies are only bound when they are actually loaded.

Also add AOT + lazy-load regression coverage to LazyLoadingTests, which
previously only exercised the interpreter (Debug) configuration.

Fixes dotnet#125794

(cherry picked from commit 3816e04)
Copilot AI review requested due to automatic review settings July 19, 2026 14:25
@github-actions github-actions Bot added the area-AssemblyLoader-coreclr only use for closed issues label Jul 19, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This backport fixes a Blazor WebAssembly startup crash when AOT is combined with BlazorWebAssemblyLazyLoad by automatically enabling Mono’s existing --aot-lazy-assembly-load runtime option in that specific configuration. This keeps AOT images usable at startup by deferring binding to lazy assemblies until they are actually loaded.

Changes:

  • Append --aot-lazy-assembly-load to the generated WebAssembly runtime options when RunAOTCompilation=true and at least one BlazorWebAssemblyLazyLoad item is present (and the option isn’t already specified).
  • Add a new publish-time regression test that exercises Release + AOT + lazy-load and validates the app runs and produces expected output.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/mono/wasm/Wasm.Build.Tests/LazyLoadingTests.cs Adds a Release publish + AOT regression test for lazy assembly loading to prevent startup-crash regressions.
src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets Automatically enables --aot-lazy-assembly-load for AOT + BlazorWebAssemblyLazyLoad builds by updating the boot config runtime options.

@lewing lewing added area-Build-mono arch-wasm WebAssembly architecture and removed area-AssemblyLoader-coreclr only use for closed issues labels Jul 19, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara
See info in area-owners.md if you want to be subscribed.

@lewing lewing added the Servicing-consider Issue for next servicing release review label Jul 19, 2026
@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Workflow state for the Holistic Review Orchestrator.

{
  "version": 5,
  "last_dispatched_commit": "6147c9744911181d4a2c0791a21fcafbe4349f1a",
  "last_dispatched_base_ref": "release/10.0",
  "last_dispatched_base_sha": "018b2627823158a827a8222f0327f99f6a970565",
  "last_reviewed_commit": "6147c9744911181d4a2c0791a21fcafbe4349f1a",
  "last_reviewed_base_ref": "release/10.0",
  "last_reviewed_base_sha": "018b2627823158a827a8222f0327f99f6a970565",
  "last_recorded_worker_run_id": "29691594791",
  "review_attempt_commit": "",
  "review_attempt_base_ref": "",
  "review_attempt_count": 0,
  "max_review_attempts": 5,
  "review_history_format": "holistic-review-disclosure-v1",
  "review_history": [
    {
      "commit": "6147c9744911181d4a2c0791a21fcafbe4349f1a",
      "review_id": 4730965447
    }
  ]
}

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Holistic Review

Motivation: This is a clean backport of the merged main PR #131020 to release/10.0, fixing #125794. AOT-published Blazor WebAssembly apps that use BlazorWebAssemblyLazyLoad crash during Mono startup: an AOT image hard-binds to every assembly it references at load time (load_aot_module/load_image), but a lazy-loaded assembly is absent at startup, so the referencing image is marked unusable and its methods fall back to the interpreter, which then aborts on an unsupported construct. The motivation is well substantiated by the linked issue and the root-cause explanation.

Approach: The fix appends the existing runtime option --aot-lazy-assembly-load to _BlazorWebAssemblyRuntimeOptions from the WebAssembly SDK targets, gated on RunAOTCompilation=true AND at least one BlazorWebAssemblyLazyLoad item, and only when the option is not already present. This defers binding of referenced lazy assemblies until they are actually loaded. It reuses an already-supported runtime option rather than adding new runtime machinery, and the MSBuild condition is correctly scoped so non-AOT and non-lazy builds are unaffected. A native-mono regression test (LoadLazyAssemblyWithAOT) exercises the Release + AOT + lazy-load combination that the existing Debug tests do not cover.

Summary: LGTM. The change is minimal, narrowly scoped, and matches the merged main PR (cherry-pick of 3816e04) exactly (28 additions, targets + test only). The MSBuild condition correctly checks all three predicates (AOT enabled, lazy-load items present, option not already set) and idempotently appends the flag with a .Trim() to avoid leading/trailing whitespace. The added test asserts the expected JSON output that only appears when the runtime initializes and lazily loads the assembly successfully, providing meaningful regression coverage. No correctness, safety, or performance concerns for a backport of this size, and the risk is appropriately assessed as low. No actionable findings.

Note

This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.

Generated by Holistic Review · 52.1 AIC · ⌖ 16 AIC · ⊞ 10K

@lewing lewing added this to the 10.0.x milestone Jul 19, 2026
@rbhanda rbhanda modified the milestones: 10.0.x, 10.0.12 Jul 20, 2026
@rbhanda rbhanda added Servicing-approved Approved for servicing release and removed Servicing-consider Issue for next servicing release review labels Jul 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture area-Build-mono Servicing-approved Approved for servicing release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants