Description
Is there an existing issue for this?
- I have searched the existing issues
Describe the bug
When attempting to call DotNet.invokeMethodAsync
from a JavaScript file backing a custom Web Component during its initialization (for example, inside connectedCallback
), the following error is thrown:
Error: Assertion failed - heap is currently locked
at mr (blazor.web.js:1:158963)
at Object.beginInvokeDotNetFromJS
This happens in a Blazor WebAssembly project.The same approach works fine in a .NET MAUI Blazor Hybrid app.
I'm aware that calling .NET from JS during component initialization isn't considered a good practice, but invoking .NET methods would help a gradual migration of a legacy web app to Blazor and MAUI. The goal is to integrate Blazor as a backend facade while preserving the existing UI for now, enabling the team to write new code in Blazor moving forward.
The documentation does not appear to impose any restriction that prevents from using this to handle JS calls: Link to docs.
Minimal repro repo
https://github.com/alexuaua/BlazorDotNetInteropDemo
Steps to reproduce
- Clone the repo.
- Run the project and switch to the Counter page.
- Open the browser console.
- Observe the
heap is currently locked
error (only) during initialization of the web component.
Expected behaviour
The JS-to-.NET interop call should succeed (as it does in MAUI Blazor), or there should be a documented workaround for such use cases. Alternatively, the documentation should clearly explain the restrictions on using DotNet.invokeMethodAsync in Blazor WebAssembly apps.
Actual behaviour
The interop call fails due to what appears to be a race condition.
Deferring the call using setTimeout
(i.e. placing the call later in the stack) resolves the issue, this workaround is even shown in community resources like Blazor University.
That said, it would be very helpful if:
- The official documentation explicitly described this race condition and recommended patterns for
DotNet.invokeMethodAsync
in component scenarios. - Blazor could provide a way to queue interop calls that are attempted during initialization - would be helpful for migration projects where modifying component logic is non-trivial.
Unless I’m missing something, the tasks performed in the demo do not require synchronous execution. Blazor WebAssembly’s rendering engine is based on its own component lifecycle and does not track or wait for hydration of external DOM elements, such as those created by Web Components.
This issue blocks a real-world migration scenario where we cannot immediately rewrite all UI in Blazor. Any workaround or clarification on supported interop timing would be appreciated.
Related issues
- dotnet/aspnetcore#26809 also a race condition, but unrelated to JS interop.
Environment
- .NET SDK: 9
- Browser: Chrome / Edge (latest)
- App: Blazor WebAssembly
Expected Behavior
No response
Steps To Reproduce
No response
Exceptions (if any)
No response
.NET Version
No response
Anything else?
No response