dotnet/wpf · error · InvalidOperationException

SR.MediaContext_InfiniteLayoutLoop

Error message

SR.MediaContext_InfiniteLayoutLoop

What it means

The MediaContext render-callback drain loop (reached via the public render path) caps iterations at 153; if handlers queued via Dispatcher/Render keep re-registering Render-on-load callbacks so the list never empties, it throws InvalidOperationException(SR.MediaContext_InfiniteLayoutLoop). It detects endless layout/arrange feedback in a single render pass.

Solutions

  1. Find the element invalidating itself during render/layout (use WPF Visual Studio diagnostics / Layout rounding breakpoints) and remove that call.
  2. Move InvalidateArrange/InvalidateMeasure calls out of OnRender into event handlers that run once (Loaded, PropertyChanged) with re-entrancy guards.
  3. Use a boolean flag to prevent re-triggering layout while the same pass is in flight.
  4. Coalesce repeated invalidations; only invalidate when a value actually changed (compare old/new).

Example fix

// before
protected override void OnRender(DrawingContext dc) {
    base.OnRender(dc);
    InvalidateMeasure(); // endless layout loop
}
// after
private double _lastValue;
protected override void OnRender(DrawingContext dc) {
    base.OnRender(dc);
    if (Math.Abs(Value - _lastValue) > 0.001) { _lastValue = Value; InvalidateMeasure(); }
}
Defensive patterns

Strategy: validation

Validate before calling

private bool _layoutPending;
void RequestLayout() {
  if (_layoutPending) return; // coalesce; prevents endless invalidate cycle
  _layoutPending = true;
  Dispatcher.BeginInvoke(() => { _layoutPending = false; InvalidateMeasure(); });
}

Try / catch

try { DoCustomLayout(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("layout")) { BreakInvalidationCycle(); Log("infinite layout loop"); }

Prevention

When it happens

Trigger: A single Dispatcher render pass where InvokeOnRender callbacks (registered via CompositionTarget.Rendering-like Render hooks or InvalidateArrange in OnRender) keep adding new callbacks each iteration past 153 rounds.

Common situations: Code that calls InvalidateMeasure/InvalidateArrange from OnRender or a render callback of the same element, custom controls invalidating each other in MeasureOverride/ArrangeOverride cycles, or LayoutUpdated handlers forcing another layout.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/MediaContext.cs:1928

        /// <summary>
        /// Calls all _invokeOnRenderCallbacks until no more are added
        /// </summary>
        private void FireInvokeOnRenderCallbacks()
        {
            int callbackLoopCount = 0;
            int count = InvokeOnRenderCallbacksCount;

            // This outer loop is to re-run layout in case the app causes a layout to get enqueued in response
            // to a Loaded event. In this case we would like to re-run layout before we allow render.
            do
            {
                while (count > 0)
                {
                    callbackLoopCount++;
                    if (callbackLoopCount > 153)
                    {
                        throw new InvalidOperationException(SR.MediaContext_InfiniteLayoutLoop);
                    }

                    FrugalObjectList<InvokeOnRenderCallback> callbacks = _invokeOnRenderCallbacks;
                    _invokeOnRenderCallbacks = null;

                    for (int i = 0; i < count; i++)
                    {
                        callbacks[i].DoWork();
                    }

                    count = InvokeOnRenderCallbacksCount;
                }

                // Fire all the pending Loaded events before Render happens
                // but after the layout storm has subsided
                FireLoadedPendingCallbacks();

                count = InvokeOnRenderCallbacksCount;

View on GitHub (pinned to 81131a70a4)