dotnet/wpf · error · InvalidOperationException
SR.ReentrantVisualTreeChangeError
Error message
SR.ReentrantVisualTreeChangeError
What it means
VisualDiagnostics.VerifyVisualTreeChangeCore throws InvalidOperationException when code attempts to modify the visual tree while a VisualTreeChanged callback is in progress and the change is not explicitly allowed. Re-entrant mutation during change notification would produce inconsistent notifications and corrupt the diagnostic event stream, so WPF blocks it.
Solutions
- Defer the mutation out of the callback: Dispatcher.BeginInvoke the tree change
- Return from the handler before mutating; queue changes and apply after Changed completes
- Call EnableHelper.AllowChangesDuringVisualTreeChanged if re-entrancy is intentional (diagnostics infrastructure only)
- Disable VisualDiagnostics tooling in the affected path
Example fix
// before
private void OnVisualTreeChanged(object sender, VisualTreeChangeEventArgs e)
{
((Panel)e.Parent).Children.Remove((UIElement)e.Child);
}
// after
private void OnVisualTreeChanged(object sender, VisualTreeChangeEventArgs e)
{
Dispatcher.BeginInvoke(() => ((Panel)e.Parent).Children.Remove((UIElement)e.Child));
} Defensive patterns
Strategy: try-catch
Validate before calling
if (VisualDiagnostics.IsEnabled && isInsideVisualTreeChangedHandler)
{
Dispatcher.BeginInvoke(() => ApplyTreeChange());
} Type guard
bool CanMutateTreeNow => !isInsideVisualTreeChangedHandler;
Try / catch
try { panel.Children.Add(child); }
catch (InvalidOperationException ex) when (ex.Message.Contains("VisualTreeChanged"))
{
Dispatcher.BeginInvoke(() => panel.Children.Add(child));
} Prevention
- Never mutate the visual tree synchronously inside a VisualTreeChanged handler
- Use Dispatcher.BeginInvoke to defer diagnostics-driven tree edits
- Track whether a Changed callback is active with a flag in tooling code
When it happens
Trigger: Modifying the visual tree (adding/removing children, applying templates) inside a VisualTreeChanged event handler; changes triggered indirectly from the handler (e.g. setting a property that alters the tree).
Common situations: Debug/inspector tools that react to VisualTreeChanged by immediately editing the tree; property change handlers that add/remove elements while diagnostics are enabled (ENABLE_XAML_DIAGNOSTICS_SOURCE_INFO).
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- SR.Automation_RecursivePublicCall
- SR.MethodCallNotAllowed
- ' ' is not a Visual or Visual3D.
- Cannot countersign an unsigned package.
- Cannot read Page properties because it is not in a tree…
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/9a51d9bf05fc6660.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Diagnostics/VisualDiagnostics.cs:213
// detect whether a VisualTreeChanged event is in progress. If so,
// throw an exception unless overridden by the app-context flag.
internal static void VerifyVisualTreeChange(DependencyObject d)
{
// write this so that the 90% case is inlined - check the flag and move on
if (s_HasVisualTreeChangedListeners)
{
VerifyVisualTreeChangeCore(d);
}
}
private static void VerifyVisualTreeChangeCore(DependencyObject d)
{
if (s_IsVisualTreeChangedInProgress)
{
if (!EnableHelper.AllowChangesDuringVisualTreeChanged(d))
{
throw new InvalidOperationException(SR.Format(SR.ReentrantVisualTreeChangeError, nameof(VisualTreeChanged)));
}
}
}
internal static bool IsEnvironmentVariableSet(string value, string environmentVariable)
{
if (value != null)
{
return IsEnvironmentValueSet(value);
}
value = Environment.GetEnvironmentVariable(environmentVariable);
return IsEnvironmentValueSet(value);
}
internal static bool IsEnvironmentValueSet(string value)
{View on GitHub (pinned to 81131a70a4)