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
- Defer the AddLogicalChild call with Dispatcher.BeginInvoke/BeginInvoke(DispatcherPriority.Background) so it runs after the tree walk completes.
- Move child-adding logic out of property-changed callbacks into OnApplyTemplate, Loaded, or initialization.
- 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
- Never add logical children from OnPropertyChanged or other property-invalidation callbacks.
- Defer tree mutations with Dispatcher.BeginInvoke when reacting to property changes.
- Perform child construction in OnApplyTemplate/Loaded instead of reactive handlers.
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
- SR.CannotBeSelfParent
- SR.CannotBeSelfParent
- SR.CannotModifyLogicalChildrenDuringTreeWalk
- SR.CannotModifyLogicalChildrenDuringTreeWalk
- SR.CannotModifyVisualChildrenDuringTreeWalk
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)