Skip to content

NRE in CompositionTarget.HitTestCore from PointerOverPreProcessor.SceneInvalidated #21286

@Symbai

Description

@Symbai

Describe the bug

  • Avalonia version: 11.3.13
  • OS: Windows 10.0.19045 (also reported under Wine / Steam Deck — Z:\home\deck\...)
  • Runtime: .NET 10.0.5, NativeAOT (RuntimeFeature.IsDynamicCodeSupported == false)
  • Renderer: default compositing renderer (Win32 backend)

Summary

An unobserved NullReferenceException is raised from inside the renderer's hit-test path when the pointer-over preprocessor reacts to a SceneInvalidated event. The exception is swallowed by the dispatcher (it surfaces only via TaskScheduler.UnobservedTaskException), so the app does not crash, but it is logged as an unhandled fault by crash-reporting SDKs (Sentry in our case).

Stack trace

System.NullReferenceException: Object reference not set to an instance of an object.
   at Avalonia.Rendering.Composition.CompositionTarget.HitTestCore(CompositionVisual, Point, PooledList`1, Func`2)
   at Avalonia.Rendering.Composition.CompositionTarget.HitTestCore(...)   [~50x recursion through visual tree]
   at Avalonia.Rendering.Composition.CompositionTarget.TryHitTest(Point, CompositionVisual, Func`2)
   at Avalonia.Rendering.Composition.CompositingRenderer.<HitTest>d__25.MoveNext()
   at System.Linq.Enumerable.TryGetFirstNonIterator[TSource](IEnumerable`1, out bool)
   at Avalonia.VisualTree.VisualExtensions.GetVisualAt(Visual, Point, Func`2)
   at Avalonia.Input.InputExtensions.InputHitTest(IInputElement, Point, bool)
   at Avalonia.Input.PointerOverPreProcessor.SceneInvalidated(Rect)
   at Avalonia.Controls.TopLevel.SceneInvalidated(object sender, SceneInvalidatedEventArgs e)
   at Avalonia.Threading.DispatcherOperation.InvokeCore()

Wrapped as: AggregateExceptionNullReferenceException via UnobservedTaskException (HResult 0x80004003 inside, 0x80131500 outer).

Analysis

The NRE happens during the recursive HitTestCore walk after the renderer has dispatched a SceneInvalidated callback to the UI thread. By the time the dispatcher op runs, some CompositionVisual/child reference in the tree appears to be null - consistent with hit-testing a scene that is concurrently being torn down (a TopLevel closing, a control being unloaded, or a visual subtree being detached) while the pointer-over preprocessor is replaying the last known cursor position.

The dispatcher op is fire-and-forget, so the exception is observed only when the resulting Task is finalized - it does not crash the app, but it is a real bug in framework code (no user code is on the stack).

Suggested fix direction

PointerOverPreProcessor.SceneInvalidated (and/or CompositionTarget.HitTestCore) should be defensive against a partially-detached/disposed visual subtree - either skip the hit test when the target TopLevel / root composition visual is no longer attached, or null-guard the child traversal in HitTestCore.

Notes

  • Crash reports reach us via Sentry's UnobservedTaskException integration; the user does not see a crash.
  • One reporter is on Wine/Proton (Steam Deck) where event timing differs from native Win32 and may widen the race window. Mentioning in case it helps reproduce.
  • Project is published with NativeAOT, but the stack is entirely managed and I don't believe AOT is causally relevant.

To Reproduce

I do not have a tight standalone repro yet. In our app the trace is most consistent with: a window/TopLevel closing or a major visual-tree swap occurring while the cursor is over the affected window. It is intermittent and we have only seen it in the wild (production crash reports), not locally.

Expected behavior

No response

Avalonia version

11.3.13

OS

Linux

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions