dotnet/wpf · error · InvalidOperationException

SR.Format(SR.LayoutManager_DeepRecursion…

Error message

SR.Format(SR.LayoutManager_DeepRecursion, s_LayoutRecursionLimit)

What it means

The WPF LayoutManager enforces s_LayoutRecursionLimit on nested Measure calls; when _measuresOnStack exceeds that limit it throws InvalidOperationException. This guards against infinite measure recursion (an element triggering its own re-measure synchronously) which would otherwise stack-overflow.

Solutions

  1. Find the cycle: check for calls to UpdateLayout/Measure/InvalidateMeasure inside MeasureOverride or property-change handlers and move them out (defer with Dispatcher.BeginInvoke)
  2. Break reciprocal layout dependencies between parent and child so measure does not re-trigger measure on the same tree path
  3. Reduce visual-tree nesting depth (flatten nested ItemsControls/panels, virtualize lists)
  4. If depth is legitimate, refactor to an iterative layout approach — the recursion limit cannot be raised

Example fix

// before
protected override Size MeasureOverride(Size availableSize)
{
    UpdateLayout(); // re-enters measure -> DeepRecursion
    ...
}
// after
protected override Size MeasureOverride(Size availableSize)
{
    Dispatcher.BeginInvoke(() => child.InvalidateMeasure(), DispatcherPriority.Loaded);
    ...
}
Defensive patterns

Strategy: try-catch

Try / catch

try { element.Measure(availableSize); }
catch (InvalidOperationException ex) when (ex.Message.Contains("recursion") || ex.Message.Contains("layout")) { Debugger.Break(); /* diagnose cycle */ }

Prevention

When it happens

Trigger: A MeasureOverride (or layout callback on the measure path) synchronously invalidates and re-measures an ancestor/descendant in a cycle — e.g. calling UpdateLayout or Measure inside MeasureOverride, or a circular size-negotiation loop between parent and child.

Common situations: Custom panels that call InvalidateMeasure/UpdateLayout on other elements inside MeasureOverride; third-party controls with cyclical layout dependencies; data-bound size changes that fire layout synchronously; deeply recursive visual trees such as nested ItemsControls or recursion-generating templates.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/dfce21983a567ce1. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/LayoutManager.cs:132

            ((ContextLayoutManager)arg).NeedsRecalc();
            return null;
        }

        private bool hasDirtiness
        {
            get
            {
                return (!MeasureQueue.IsEmpty) || (!ArrangeQueue.IsEmpty);
            }
        }

        internal void EnterMeasure()
        {
            Dispatcher._disableProcessingCount++;
            _lastExceptionElement = null;
            _measuresOnStack++;
            if(_measuresOnStack > s_LayoutRecursionLimit)
                throw new InvalidOperationException(SR.Format(SR.LayoutManager_DeepRecursion, s_LayoutRecursionLimit));

            _firePostLayoutEvents = true;
        }

        internal void ExitMeasure()
        {
            _measuresOnStack--;
            Dispatcher._disableProcessingCount--;
        }

        internal void EnterArrange()
        {
            Dispatcher._disableProcessingCount++;
            _lastExceptionElement = null;
            _arrangesOnStack++;
            if(_arrangesOnStack > s_LayoutRecursionLimit)
                throw new InvalidOperationException(SR.Format(SR.LayoutManager_DeepRecursion, s_LayoutRecursionLimit));

View on GitHub (pinned to 81131a70a4)