{"record":{"id":"227064fb391ca45f","repo":"dotnet/aspnetcore","slug":"component-parameters-may-only-be-added-immediately","errorCode":null,"errorMessage":"Component parameters may only be added immediately after frames of type {RenderTreeFrameType.Component}","messagePattern":"Component parameters may only be added immediately after frames of type (.+?)","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Components/Components/src/Rendering/RenderTreeBuilder.cs","lineNumber":732,"sourceCode":"    {\n        var indexOfEntryBeingClosed = _openElementIndices.Pop();\n        _entries.Buffer[indexOfEntryBeingClosed].RegionSubtreeLengthField = _entries.Count - indexOfEntryBeingClosed;\n    }\n\n    private void AssertCanAddAttribute()\n    {\n        if (_lastNonAttributeFrameType != RenderTreeFrameType.Element\n            && _lastNonAttributeFrameType != RenderTreeFrameType.Component)\n        {\n            throw new InvalidOperationException($\"Attributes may only be added immediately after frames of type {RenderTreeFrameType.Element} or {RenderTreeFrameType.Component}\");\n        }\n    }\n\n    private void AssertCanAddComponentParameter()\n    {\n        if (_lastNonAttributeFrameType != RenderTreeFrameType.Component)\n        {\n            throw new InvalidOperationException($\"Component parameters may only be added immediately after frames of type {RenderTreeFrameType.Component}\");\n        }\n    }\n\n    private int? GetCurrentParentFrameIndex()\n        => _openElementIndices.Count == 0 ? (int?)null : _openElementIndices.Peek();\n\n    private RenderTreeFrameType? GetCurrentParentFrameType()\n    {\n        var parentIndex = GetCurrentParentFrameIndex();\n        return parentIndex.HasValue\n            ? _entries.Buffer[parentIndex.Value].FrameTypeField\n            : (RenderTreeFrameType?)null;\n    }\n\n    /// <summary>\n    /// Clears the builder.\n    /// </summary>\n    public void Clear()","sourceCodeStart":714,"sourceCodeEnd":750,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/Components/Components/src/Rendering/RenderTreeBuilder.cs#L714-L750","documentation":"RenderTreeBuilder.AddComponentParameter (and the related AssertCanAddComponentParameter guard) requires that the immediately preceding non-attribute frame be a Component frame. Blazor builds its render tree as an ordered sequence of frames; component parameters are child metadata of a component frame and are only legal while a component is the open context. Adding a parameter when the last structural frame is an Element, Region, or nothing at all corrupts the tree, so the builder throws InvalidOperationException immediately.","triggerScenarios":"Calling builder.AddComponentParameter(...) before builder.OpenComponent(...), or after builder.CloseComponent(), or after builder.OpenElement(...) without opening a component. Also triggered by calling AddComponentParameter after OpenRegion or at the root of an empty builder, because _lastNonAttributeFrameType is not RenderTreeFrameType.Component.","commonSituations":"Hand-written RenderFragment delegates that build markup imperatively; copy-pasting element attribute code (AddAttribute) into a component path and forgetting to swap to AddComponentParameter ordering; refactoring a component fragment and leaving a stray AddComponentParameter call after a CloseComponent; runtime-emitted trees from dynamic component generation libraries.","solutions":["Ensure every AddComponentParameter call is preceded by an unmatched OpenComponent<T> on the same builder, with no CloseComponent in between.","Replace any AddComponentParameter intended for an element with AddAttribute (element attributes use AddAttribute; only component parameters use AddComponentParameter).","Audit the RenderFragment for an uneven Open/Close sequence — every OpenComponent must pair with exactly one CloseComponent before the fragment returns.","If generating trees dynamically, track open-frame depth yourself and assert it before emitting a parameter."],"exampleFix":"// before\nbuilder.OpenElement(0, \"div\");\nbuilder.AddComponentParameter(1, \"Foo\", 42); // throws\n// after\nbuilder.OpenComponent<MyComponent>(0);\nbuilder.AddComponentParameter(1, \"Foo\", 42);\nbuilder.CloseComponent();","handlingStrategy":"validation","validationCode":"// Before emitting a component parameter, ensure the builder is inside an open component.\n// RenderTreeBuilder does not expose _lastNonAttributeFrameType publicly, so the\n// discipline is structural: pair every AddComponentParameter with a preceding OpenComponent.\nstatic bool IsInsideComponentBranch(Stack<RenderTreeFrameType> openFrames)\n    => openFrames.Count > 0 && openFrames.Peek() == RenderTreeFrameType.Component;","typeGuard":"// Static guard for hand-written render fragments: track open-frame type yourself.\nreadonly struct RenderScope : IDisposable {\n    public RenderTreeFrameType FrameType { get; }\n    public RenderScope(RenderTreeFrameType t) => FrameType = t;\n    public void Dispose() { }\n}\nstatic RenderScope ComponentScope<T>(RenderTreeBuilder b, int seq) {\n    b.OpenComponent<T>(seq); return new(RenderTreeFrameType.Component);\n}","tryCatchPattern":null,"preventionTips":["Always pair OpenComponent<T> / CloseComponent around AddComponentParameter calls.","Use AddAttribute for element attributes, AddComponentParameter for component parameters — never mix.","Extract sub-trees into named RenderFragment methods so each fragment is balanced in isolation.","Run the component in a bUnit test; bUnit surfaces invalidOperationException from BuildRenderTree."],"tags":["blazor","render-tree","renderfragment","invalidoperationexception"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}