dotnet / dotnet/aspnetcore

Blazor `DotNet.invokeMethodAsync` from a web component triggers `heap is currently locked` error

Open
#62,476 0 comments 0 reactions 0 assignees View on GitHub
area-blazor
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 6h
Merged PRs (30d)
290

Description

### Is there an existing issue for this?

- [x] 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.](https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/call-dotnet-from-javascript?view=aspnetcore-9.0#invoke-a-static-net-method)

## Minimal repro repo
[https://github.com/alexuaua/BlazorDotNetInteropDemo](https://github.com/alexuaua/BlazorDotNetInteropDemo)

## Steps to reproduce
1. Clone the repo.
2. Run the project and switch to the Counter page.
3. Open the browser console.
4. 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](https://blazor-university.com/javascript-interop/calling-dotnet-from-javascript/calling-static-dotnet-methods/).

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](https://github.com/dotnet/aspnetcore/issues/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_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.