{"record":{"id":"786bb2b9371709fb","repo":"dotnet/wpf","slug":"sr-mediacontext-infinitelayoutloop","errorCode":null,"errorMessage":"SR.MediaContext_InfiniteLayoutLoop","messagePattern":"SR\\.MediaContext_InfiniteLayoutLoop","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/MediaContext.cs","lineNumber":1928,"sourceCode":"\n        /// <summary>\n        /// Calls all _invokeOnRenderCallbacks until no more are added\n        /// </summary>\n        private void FireInvokeOnRenderCallbacks()\n        {\n            int callbackLoopCount = 0;\n            int count = InvokeOnRenderCallbacksCount;\n\n            // This outer loop is to re-run layout in case the app causes a layout to get enqueued in response\n            // to a Loaded event. In this case we would like to re-run layout before we allow render.\n            do\n            {\n                while (count > 0)\n                {\n                    callbackLoopCount++;\n                    if (callbackLoopCount > 153)\n                    {\n                        throw new InvalidOperationException(SR.MediaContext_InfiniteLayoutLoop);\n                    }\n\n                    FrugalObjectList<InvokeOnRenderCallback> callbacks = _invokeOnRenderCallbacks;\n                    _invokeOnRenderCallbacks = null;\n\n                    for (int i = 0; i < count; i++)\n                    {\n                        callbacks[i].DoWork();\n                    }\n\n                    count = InvokeOnRenderCallbacksCount;\n                }\n\n                // Fire all the pending Loaded events before Render happens\n                // but after the layout storm has subsided\n                FireLoadedPendingCallbacks();\n\n                count = InvokeOnRenderCallbacksCount;","sourceCodeStart":1910,"sourceCodeEnd":1946,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/MediaContext.cs#L1910-L1946","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Find the element invalidating itself during render/layout (use WPF Visual Studio diagnostics / Layout rounding breakpoints) and remove that call.","Move InvalidateArrange/InvalidateMeasure calls out of OnRender into event handlers that run once (Loaded, PropertyChanged) with re-entrancy guards.","Use a boolean flag to prevent re-triggering layout while the same pass is in flight.","Coalesce repeated invalidations; only invalidate when a value actually changed (compare old/new)."],"exampleFix":"// before\nprotected override void OnRender(DrawingContext dc) {\n    base.OnRender(dc);\n    InvalidateMeasure(); // endless layout loop\n}\n// after\nprivate double _lastValue;\nprotected override void OnRender(DrawingContext dc) {\n    base.OnRender(dc);\n    if (Math.Abs(Value - _lastValue) > 0.001) { _lastValue = Value; InvalidateMeasure(); }\n}","handlingStrategy":"validation","validationCode":"private bool _layoutPending;\nvoid RequestLayout() {\n  if (_layoutPending) return; // coalesce; prevents endless invalidate cycle\n  _layoutPending = true;\n  Dispatcher.BeginInvoke(() => { _layoutPending = false; InvalidateMeasure(); });\n}","typeGuard":null,"tryCatchPattern":"try { DoCustomLayout(); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"layout\")) { BreakInvalidationCycle(); Log(\"infinite layout loop\"); }","preventionTips":["Never call InvalidateMeasure/InvalidateArrange from OnRender","Only invalidate when a value actually changed (compare before assigning)","Avoid cross-invalidating controls from MeasureOverride/ArrangeOverride","Use LayoutUpdated only for observation, never for triggering layout"],"tags":["wpf","layout","infinite-loop","render-callback"],"backgroundTag":"internal-invariant-violation","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-21T21:30:21.729Z"}