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

  1. Defer the mutation out of the callback: Dispatcher.BeginInvoke the tree change
  2. Return from the handler before mutating; queue changes and apply after Changed completes
  3. Call EnableHelper.AllowChangesDuringVisualTreeChanged if re-entrancy is intentional (diagnostics infrastructure only)
  4. 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

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


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)