Skip to content

[release/10.0] Fix null reference error in Virtualize component#66726

Merged
wtgodbe merged 4 commits into
release/10.0from
backport/pr-65207-to-release/10.0
May 21, 2026
Merged

[release/10.0] Fix null reference error in Virtualize component#66726
wtgodbe merged 4 commits into
release/10.0from
backport/pr-65207-to-release/10.0

Conversation

@github-actions

@github-actions github-actions Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

Backport of #65207 to release/10.0

/cc @oroztocil @ilonatommy @javiercn

Fix null reference error in Virtualize component

Guard Virtualize.ts interop against spacers removed before init() runs.

Description

When a Virtualize component is rapidly shown/hidden (e.g. inside an autocomplete dropdown), the JS interop Blazor._internal.Virtualize.init() call can arrive after the component has been removed from the DOM. Blazor resolves ElementReference on the JS side via querySelector(...) at call time, so the spacer arguments can be null (or refer to elements that are no longer connected), causing:

TypeError: Cannot read properties of null (reading 'parentElement')
  at VirtualizeJsInterop.InitializeAsync
  at Virtualize`1.OnAfterRenderAsync

The fix adds defensive guards at three points in Virtualize.ts:

  1. Early return in init() if either spacer is null or not isConnected.
  2. MutationObserver callback bails out if the observed spacer is no longer connected.
  3. IntersectionObserver callback bails out if either spacer is no longer connected.

It also moves dotNetHelper.dispose() outside the if (observers) block in dispose() so the DotNetObjectReference is always released, even when init() returned early and no observers were created.

Fixes #65139

Customer Impact

Customer reported. Multiple reports of hitting this exception in a production scenario.

There is no usable workaround in user code: the error originates inside the framework's bundled JS interop, and Virtualize<T> does not expose any hook to delay or skip init.

Regression?

  • Yes
  • No

Pre-existing race condition in Virtualize JS interop, not a regression from a previous release.

Risk

  • High
  • Medium
  • Low

Only passive guards added to the TypeScript code for the Virtualize component. On the failure path the component silently no-ops instead of throwing. No public API change, no .NET-side change.

Verification

  • Manual (required)
  • Automated

Verified on main via Playwright routeWebSocket interception that selectively delays only Virtualize.init SignalR messages, reproducing the exact failure from the original report with 100% hit rate before the fix and 0 failures after. Automatic testing otherwise difficult. Existing Blazor Virtualize E2E suites continue to pass. Full investigation report attached to the PR discussion (comment).

Packaging changes reviewed?

  • Yes
  • No
  • N/A

oroztocil and others added 3 commits May 18, 2026 13:55
When a Virtualize component is rapidly shown/hidden (e.g., in an autocomplete
dropdown), the JS interop call to initialize the virtualize observers may arrive
after the component has been removed from the DOM. This causes a 'Cannot read
properties of null (reading parentElement)' error.

This fix adds null checks at three points in Virtualize.ts:
1. Early return in init() if spacerBefore or spacerAfter are null
2. Check spacer.isConnected in mutation observer callback
3. Check spacerBefore.isConnected and spacerAfter.isConnected in intersection callback

Fixes #65139
The existing null check handles the case where Blazor resolves element
references to null. This adds an isConnected check for the theoretical
scenario where references resolve to valid but disconnected elements,
which would cause errors further in the init function (e.g., when
Range.setStartAfter is called on disconnected nodes).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When init() returns early due to disconnected spacers, no observers
are stored. The existing dispose() only called dotNetHelper.dispose()
inside the 'if (observers)' block, leaking the DotNetObjectReference
until the circuit closes. In rapid toggle scenarios this accumulates.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot requested a review from a team as a code owner May 18, 2026 13:55
@github-actions github-actions Bot added the area-blazor Includes: Blazor, Razor Components label May 18, 2026
@oroztocil oroztocil added the Servicing-consider Shiproom approval is required for the issue label May 18, 2026
@oroztocil oroztocil requested review from ilonatommy and javiercn May 18, 2026 15:00
@wtgodbe wtgodbe merged commit c1a620e into release/10.0 May 21, 2026
28 checks passed
@wtgodbe wtgodbe deleted the backport/pr-65207-to-release/10.0 branch May 21, 2026 23:12
@dotnet-policy-service dotnet-policy-service Bot added this to the 10.0.9 milestone May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-blazor Includes: Blazor, Razor Components Servicing-approved Shiproom has approved the issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants