{"record":{"id":"a163c4cd70bea664","repo":"AvaloniaUI/Avalonia","slug":"infinite-layout-loop-detected","errorCode":null,"errorMessage":"Infinite layout loop detected","messagePattern":"Infinite layout loop detected","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Avalonia.Base/Media/MediaContext.cs","lineNumber":200,"sourceCode":"    }\n\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 = _invokeOnRenderCallbacks?.Count ?? 0;\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                    throw new InvalidOperationException(\"Infinite layout loop detected\");\n\n                var callbacks = _invokeOnRenderCallbacks!;\n                _invokeOnRenderCallbacks = null;\n\n                for (int i = 0; i < count; i++) \n                    callbacks[i].Invoke();\n                \n                callbacks.Clear();\n                _invokeOnRenderCallbackListPool.Push(callbacks);\n\n                count = _invokeOnRenderCallbacks?.Count ?? 0;\n            }\n\n            // TODO: port the rest of the Loaded logic later\n            // Fire all the pending Loaded events before Render happens\n            // but after the layout storm has subsided\n            // FireLoadedPendingCallbacks();\n","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/AvaloniaUI/Avalonia/blob/11c542726898ae954a1ef668c65ec79ec92ab17d/src/Avalonia.Base/Media/MediaContext.cs#L182-L218","documentation":"MediaContext's render callback loop re-runs layout when render callbacks enqueue more work (e.g. via Loaded events). A counter guards against runaway feedback: if the loop runs more than 153 times in one pass it throws InvalidOperationException(\"Infinite layout loop detected\"), indicating app code is continuously re-invalidating layout.","triggerScenarios":"A Loaded/SizeChanged/LayoutManager callback that synchronously triggers another invalidation — e.g. measuring/arranging changes size each pass, calling InvalidateMeasure/InvalidateArrange from within a layout override in a way that never converges, or enqueueing render callbacks that themselves enqueue callbacks.","commonSituations":"A control whose MeasureOverride/ArrangeOverride returns different sizes each call; calling InvalidateMeasure inside ArrangeOverride; binding loops that resize on every layout; Loaded handlers that mutate layout-affecting properties.","solutions":["Make MeasureOverride/ArrangeOverride deterministic: return stable desired/arranged sizes for the same input.","Do not call InvalidateMeasure/InvalidateArrange from within layout overrides.","Break the feedback by deferring layout-affecting mutations to a dispatcher Post (lower priority) instead of inline.","Audit Loaded handlers for property assignments that re-trigger layout."],"exampleFix":"// before - non-converging layout\nprotected override Size MeasureOverride(Size av)\n{\n    _counter++;\n    if (_counter % 2 == 0) InvalidateMeasure(); // never converges\n    return new Size(_counter, 0);\n}\n\n// after - deterministic\nprotected override Size MeasureOverride(Size av)\n    => new Size(Math.Min(av.Width, _desired), _fixedHeight);","handlingStrategy":"validation","validationCode":"// Ensure layout overrides are deterministic and do not re-invalidate inline.\nprotected override Size MeasureOverride(Size available)\n{\n    // No InvalidateMeasure/InvalidateArrange here; return a stable size.\n    return new Size(Math.Min(available.Width, _desiredWidth), _desiredHeight);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never call InvalidateMeasure/InvalidateArrange from within layout overrides.","Make MeasureOverride/ArrangeOverride return stable sizes for the same inputs.","Defer layout-affecting mutations to Dispatcher.Post to break feedback loops.","Audit Loaded handlers for properties that re-trigger layout."],"tags":["layout","rendering","invalidoperation","infinite-loop"],"backgroundTag":null,"analyzedSha":"11c542726898ae954a1ef668c65ec79ec92ab17d","analyzedAt":"2026-08-13T11:57:40.261Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}