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 that collection. Doing so would invalidate the enumerator mid-walk, so WPF throws InvalidOperationException (CannotModifyLogicalChildrenDuringTreeWalk).

Solutions

  1. Defer the AddLogicalChild call with Dispatcher.BeginInvoke/BeginInvoke(DispatcherPriority.Background) so it runs after the tree walk completes.
  2. Move child-adding logic out of property-changed callbacks into OnApplyTemplate, Loaded, or initialization.
  3. Restructure so the collection is not changed reactively during invalidation (e.g. set a flag and add children on the next layout pass).

Example fix

// before
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
    if (e.Property == ItemsSourceProperty)
        AddLogicalChild(BuildChild()); // throws during tree walk
}

// after
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
    if (e.Property == ItemsSourceProperty)
        Dispatcher.BeginInvoke(() => AddLogicalChild(BuildChild()));
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (parent.IsLogicalChildrenIterationInProgress)
    Dispatcher.BeginInvoke(() => parent.AddLogicalChild(child));
else
    parent.AddLogicalChild(child);

Try / catch

try
{
    parent.AddLogicalChild(child);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("logical children"))
{
    Dispatcher.BeginInvoke(() => parent.AddLogicalChild(child)); // retry after the walk
}

Prevention

When it happens

Trigger: Calling AddLogicalChild from within a callback that runs during a property invalidation tree walk — e.g. inside OnPropertyChanged, OnApplyTemplate template expansion triggered by the walk, or a property-changed handler that attaches children.

Common situations: Lazy template/applying default children inside property-changed callbacks; adding child elements in response to an inherited property change ( DataContext, inherited attached properties); layout code triggered synchronously during the walk.

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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Generated/FrameworkElement.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)