dotnet/aspnetcore · error · InvalidOperationException
Component parameters may only be added immediately after…
Error message
Component parameters may only be added immediately after frames of type {RenderTreeFrameType.Component} What it means
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.
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.
Example fix
// before builder.OpenElement(0, "div"); builder.AddComponentParameter(1, "Foo", 42); // throws // after builder.OpenComponent<MyComponent>(0); builder.AddComponentParameter(1, "Foo", 42); builder.CloseComponent();
Defensive patterns
Strategy: validation
Validate before calling
// Before emitting a component parameter, ensure the builder is inside an open component.
// RenderTreeBuilder does not expose _lastNonAttributeFrameType publicly, so the
// discipline is structural: pair every AddComponentParameter with a preceding OpenComponent.
static bool IsInsideComponentBranch(Stack<RenderTreeFrameType> openFrames)
=> openFrames.Count > 0 && openFrames.Peek() == RenderTreeFrameType.Component; Type guard
// Static guard for hand-written render fragments: track open-frame type yourself.
readonly struct RenderScope : IDisposable {
public RenderTreeFrameType FrameType { get; }
public RenderScope(RenderTreeFrameType t) => FrameType = t;
public void Dispose() { }
}
static RenderScope ComponentScope<T>(RenderTreeBuilder b, int seq) {
b.OpenComponent<T>(seq); return new(RenderTreeFrameType.Component);
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Render output is invalid for component of type
- The frame at index is of type ' ', not ' '.
- The component requires a value for the parameter .
- requires a non-null value either for ' ' or ' '.
- requires that ' ' and ' ' cannot both have non-null values.
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/227064fb391ca45f.
Report an issue: GitHub.
Appendix: source
Thrown at src/Components/Components/src/Rendering/RenderTreeBuilder.cs:732
{
var indexOfEntryBeingClosed = _openElementIndices.Pop();
_entries.Buffer[indexOfEntryBeingClosed].RegionSubtreeLengthField = _entries.Count - indexOfEntryBeingClosed;
}
private void AssertCanAddAttribute()
{
if (_lastNonAttributeFrameType != RenderTreeFrameType.Element
&& _lastNonAttributeFrameType != RenderTreeFrameType.Component)
{
throw new InvalidOperationException($"Attributes may only be added immediately after frames of type {RenderTreeFrameType.Element} or {RenderTreeFrameType.Component}");
}
}
private void AssertCanAddComponentParameter()
{
if (_lastNonAttributeFrameType != RenderTreeFrameType.Component)
{
throw new InvalidOperationException($"Component parameters may only be added immediately after frames of type {RenderTreeFrameType.Component}");
}
}
private int? GetCurrentParentFrameIndex()
=> _openElementIndices.Count == 0 ? (int?)null : _openElementIndices.Peek();
private RenderTreeFrameType? GetCurrentParentFrameType()
{
var parentIndex = GetCurrentParentFrameIndex();
return parentIndex.HasValue
? _entries.Buffer[parentIndex.Value].FrameTypeField
: (RenderTreeFrameType?)null;
}
/// <summary>
/// Clears the builder.
/// </summary>
public void Clear()View on GitHub (pinned to 3600ca084e)