NullReferenceException in Dispatcher.VerifyAccess() when using Avalonia in embedded WPF process (no Application.Start())
Problem
When using Avalonia 12.x inside an existing WPF process (e.g. Revit/Dynamo) without calling Application.Start(), setting any property on Window (e.g. Title) throws a NullReferenceException:
System.NullReferenceException: Object reference not set to an instance of an object.
at Avalonia.AvaloniaObject.VerifyAccess()
at Avalonia.AvaloniaObject.SetValue[T](StyledProperty`1 property, T value, BindingPriority priority)
at Avalonia.Controls.Window.set_Title(String value)Root Cause
The call chain:
Window.set_Title("...")→ callsAvaloniaObject.SetValue()SetValue()→ callsVerifyAccess()for thread validationVerifyAccess()→ callsDispatcher?.VerifyAccess()Dispatcher.VerifyAccess()→ accesses internal field_dispatcherImpl(or_jobRunner)- If the message pump (Win32
GetMessageloop) is not started →_dispatcherImpl = null→ NullReferenceException
Why the message pump is not started
In a standard Avalonia app, Application.Start(ClassicDesktopLifetime) starts the message pump. In an embedded scenario (inside Revit/Dynamo), the message pump is already running via WPF. Avalonia cannot start a second message pump — it conflicts with the main one.
However, Application.Initialize() and AppBuilder.SetupWithoutStarting() correctly create Application.Current and register the platform (UsePlatformDetect), but the Dispatcher remains in a partially-initialized state.
Environment
- Avalonia 12.1.2 (NuGet)
- .NET 8.0
- Revit 2027 + Dynamo (WPF process)
- Windows 10/11
Proposed Fix
Option 1: Null-check in Dispatcher.VerifyAccess() (primary)
File: src/Avalonia.Base/Threading/Dispatcher.cs
// BEFORE:
public void VerifyAccess()
{
_dispatcherImpl.VerifyAccess();
}
// AFTER:
public void VerifyAccess()
{
_dispatcherImpl?.VerifyAccess();
}Rationale: If _dispatcherImpl is null, the Dispatcher is not fully initialized (message pump not started). In this case the thread check is meaningless — skip it.
Option 2: Null-check in AvaloniaObject.VerifyAccess() (alternative)
File: src/Avalonia.Base/AvaloniaObject.cs
// BEFORE:
protected void VerifyAccess()
{
Dispatcher?.VerifyAccess();
}
// AFTER:
protected void VerifyAccess()
{
try { Dispatcher?.VerifyAccess(); }
catch (NullReferenceException) { }
}Option 3: Static flag to disable thread checks (extended)
Add a static property to Dispatcher:
public static bool SkipThreadChecks { get; set; }
public void VerifyAccess()
{
if (SkipThreadChecks) return;
_dispatcherImpl?.VerifyAccess();
}Allows the embedder to disable checks:
Avalonia.Threading.Dispatcher.SkipThreadChecks = true;How to Verify
Repro scenario (PythonNet3 + Revit)
from Avalonia import Application, AppBuilder
from Avalonia.Controls import Window
# Initialize WITHOUT Application.Start()
builder = AppBuilder.Configure(Application()) \
.UsePlatformDetect()
builder.SetupWithoutStarting()
# This should NOT crash:
window = Window()
window.Title = "Test" # This line crashes without the fix
window.Width = 400
window.Height = 300
print("OK: Window properties set without NullReferenceException")Expected result
- Before fix:
NullReferenceExceptionatwindow.Title = "Test" - After fix: Properties are set without errors
Impact
Minimal. Null-check in VerifyAccess() is a safe change. If _dispatcherImpl is null → check is skipped → no side effects. Existing apps (where message pump is running) continue to work as before.
Related Files
| File | Description |
|---|---|
src/Avalonia.Base/Threading/Dispatcher.cs |
Main Dispatcher — VerifyAccess() |
src/Avalonia.Base/AvaloniaObject.cs |
Base class — SetValue() → VerifyAccess() |
src/Avalonia.Base/Threading/Dispatcher.UIThread.cs |
Static UI thread dispatcher |
Priority
High. Without this fix, Avalonia cannot be used in embedded scenarios (inside Revit, Dynamo, AutoCAD, and other WPF applications).
Source: AvaloniaUI/Avalonia