dotnet/wpf · error · InvalidOperationException

SR.CannotModifyLogicalChildrenDuringTreeWalk

Error message

SR.CannotModifyLogicalChildrenDuringTreeWalk

What it means

AddLogicalChild refuses to modify the logical children collection while a property-invalidation tree walk is iterating it. WPF throws InvalidOperationException (CannotModifyLogicalChildrenDuringTreeWalk) because changing the collection during enumeration corrupts the walk. This is an explicit reentrancy guard.

Solutions

  1. Defer the AddLogicalChild call until after the tree walk: use Dispatcher.BeginInvoke(DispatcherPriority.Loaded, ...).
  2. Move the logical-tree change out of invalidation callbacks into explicit lifecycle points (Loaded/Initialized).
  3. Restructure so children are added before properties that trigger the walk are set.

Example fix

// before
void OnDpChanged(...) { parent.AddLogicalChild(newChild); }
// after
void OnDpChanged(...) {
  Dispatcher.BeginInvoke(DispatcherPriority.Loaded,
    new Action(() => parent.AddLogicalChild(newChild)));
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (parent.IsLogicalChildrenIterationInProgress)
    Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => parent.AddLogicalChild(child)));
else
    parent.AddLogicalChild(child);

Try / catch

try { parent.AddLogicalChild(child); } catch (InvalidOperationException ex) when (ex.Message.Contains("tree walk")) { Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => parent.AddLogicalChild(child))); }

Prevention

When it happens

Trigger: Adding a logical child from within a callback invoked during property invalidation tree traversal — e.g. a PropertyChangedCallback, OnVisualParentChanged-style notifications, or a layout/measure callback that calls AddLogicalChild.

Common situations: Deferring element creation into property-change handlers; workarounds in style/resource callbacks that reparent elements; bug fixes that mutate tree structure inside DataContext or resource-change notifications.

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/cfe66c9500f24287. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Generated/FrameworkContentElement.cs:192

        {
            return _parent;
        }

        // mark whether Add should be called *before* or *after* the element adds it to its structure
        /// <summary>
        ///     Called by an element when that element adds the given object to
        ///     its logical tree.  FrameworkElement updates the affected
        ///     logical tree parent pointers to keep in sync with this insertion
        /// </summary>
        protected internal void AddLogicalChild(object child)
        {
            if (child != null)
            {
                // It is invalid to modify the children collection that we
                // might be iterating during a property invalidation tree walk.
                if (IsLogicalChildrenIterationInProgress)
                {
                    throw new InvalidOperationException(SR.CannotModifyLogicalChildrenDuringTreeWalk);
                }

                // Now that the child is going to be added, the FE/FCE construction is considered finished,
                // so we do not expect a change of InheritanceBehavior property,
                // so we can pick up properties from styles and resources.
                TryFireInitialized();

                bool exceptionThrown = true;
                try
                {
                    HasLogicalChildren = true;

                    // Child is present; reparent him to this element
                    FrameworkObject fo = new FrameworkObject(child as DependencyObject);
                    fo.ChangeLogicalParent(this);

                    exceptionThrown = false;
                }

View on GitHub (pinned to 81131a70a4)