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
- Defer the AddLogicalChild call until after the tree walk: use Dispatcher.BeginInvoke(DispatcherPriority.Loaded, ...).
- Move the logical-tree change out of invalidation callbacks into explicit lifecycle points (Loaded/Initialized).
- 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
- Never mutate the logical tree inside property-change/invalidation callbacks; defer via Dispatcher.
- Restructure code so structural changes happen in Loaded/Initialized lifecycle points.
- Add a unit test that triggers the invalidation path to catch reentrancy early.
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
- SR.CannotModifyLogicalChildrenDuringTreeWalk
- SR.CannotModifyVisualChildrenDuringTreeWalk
- ' ' must be the root element of a tree, but has a logical…
- Processing is disabled while the Dispatcher is in this…
- SR.ArrangeReentrancyInvalid
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)