{"record":{"id":"b3264d315089cc47","repo":"dotnet/aspnetcore","slug":"render-output-is-invalid-for-component-of-type-c","errorCode":null,"errorMessage":"Render output is invalid for component of type '{component.GetType().FullName}'. A frame of type '{invalidFrame.FrameType}' was left unclosed. Do not use try/catch inside rendering logic, because partial output cannot be undone.","messagePattern":"Render output is invalid for component of type '(.+?)'\\. A frame of type '(.+?)' was left unclosed\\. Do not use try/catch inside rendering logic, because partial output cannot be undone\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"critical","filePath":"src/Components/Components/src/Rendering/RenderTreeBuilder.cs","lineNumber":817,"sourceCode":"\n        ref var frame = ref frames[frameIndex];\n        if (frame.FrameTypeField != RenderTreeFrameType.Attribute)\n        {\n            throw new InvalidOperationException(\n                $\"The frame at index {frameIndex} is of type '{frame.FrameTypeField}', not '{RenderTreeFrameType.Attribute}'.\");\n        }\n\n        frame.AttributeValueField = value;\n    }\n\n    internal void AssertTreeIsValid(IComponent component)\n    {\n        if (_openElementIndices.Count > 0)\n        {\n            // It's never valid to leave an element/component/region unclosed. Doing so\n            // could cause undefined behavior in diffing.\n            ref var invalidFrame = ref _entries.Buffer[_openElementIndices.Peek()];\n            throw new InvalidOperationException($\"Render output is invalid for component of type '{component.GetType().FullName}'. A frame of type '{invalidFrame.FrameType}' was left unclosed. Do not use try/catch inside rendering logic, because partial output cannot be undone.\");\n        }\n    }\n\n    // Internal for testing\n    internal void ProcessDuplicateAttributes(int first)\n    {\n        Debug.Assert(_hasSeenAddMultipleAttributes);\n\n        // When AddMultipleAttributes method has been called, we need to postprocess attributes while closing\n        // the element/component. However, we also don't know the end index we should look at because it\n        // will contain nested content.\n        var buffer = _entries.Buffer;\n        var last = _entries.Count - 1;\n\n        for (var i = first; i <= last; i++)\n        {\n            if (buffer[i].FrameTypeField != RenderTreeFrameType.Attribute)\n            {","sourceCodeStart":799,"sourceCodeEnd":835,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/Components/Components/src/Rendering/RenderTreeBuilder.cs#L799-L835","documentation":"AssertTreeIsValid runs at the end of a component render and enforces that every OpenElement/OpenComponent/OpenRegion has a matching Close. The _openElementIndices stack must be empty when rendering finishes; any entry left on it means a structural frame was opened but never closed, which would cause undefined behavior in diffing. The exception names the offending component type and the leftover frame type, and explicitly warns against try/catch in rendering logic because partial tree output cannot be rolled back.","triggerScenarios":"A RenderFragment or BuildRenderTree that calls OpenElement/OpenComponent/OpenRegion but returns (or throws) before the matching CloseElement/CloseComponent/CloseRegion. Also triggered by a try/catch inside BuildRenderTree that swallows an exception and returns a half-built tree.","commonSituations":"Forgetting CloseComponent() after a conditional branch that returns early; refactoring a render method and dropping a Close call; using try/catch around Open/Close pairs to suppress an exception — the suppressed exception leaves the stack unbalanced; control-flow bugs where a loop opens elements but closes them only on the happy path.","solutions":["Audit the component's BuildRenderTree for every Open* and confirm each path — including early returns and exception handlers — reaches the matching Close*.","Remove try/catch blocks from rendering logic; if you must guard work, do it before opening frames.","Use the IDE to pair-match Open/Close calls, or extract each sub-tree into its own RenderFragment so the builder scope is bounded.","Reproduce with the offending component in isolation and step through to find the unbalanced Close."],"exampleFix":"// before\nbuilder.OpenElement(0, \"ul\");\nforeach (var item in items) {\n    if (item == null) return; // BUG: leaves <ul> open\n    builder.OpenElement(1, \"li\"); builder.AddContent(2, item); builder.CloseElement();\n}\nbuilder.CloseElement();\n// after\nbuilder.OpenElement(0, \"ul\");\nforeach (var item in items) {\n    if (item == null) continue;\n    builder.OpenElement(1, \"li\"); builder.AddContent(2, item); builder.CloseElement();\n}\nbuilder.CloseElement();","handlingStrategy":"validation","validationCode":"// Render fragments cannot self-validate mid-build, so validate the source structure:\n// every Open* must have a matching Close* on every control-flow path. Use static\n// analysis or code review; do not wrap BuildRenderTree in try/catch.","typeGuard":"// Helper to ensure balanced open/close in hand-written trees.\nusing var scope = OpenElement(builder, 0, \"div\");\n// ... children ...\n// Dispose() of the scope closes the element even on early return.\n// Implement IDisposable to call builder.CloseElement().","tryCatchPattern":"// A render-time InvalidOperationException here is unrecoverable for that render —\n// the partial tree cannot be undone. Do NOT try/catch around BuildRenderTree.\n// Instead, fix the source so the tree is always balanced, and surface the exception\n// through the host's global error boundary (<ErrorBoundary>).","preventionTips":["Never use try/catch inside BuildRenderTree — partial output cannot be undone.","Pair every OpenElement/OpenComponent/OpenRegion with a matching Close on every code path including early returns.","Use using-scope helpers (IDisposable wrappers) to guarantee Close on early returns.","Add bUnit tests for each component so an unbalanced tree fails in CI."],"tags":["blazor","render-tree","renderfragment","unclosed-frame","invalidoperationexception"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}