dotnet/aspnetcore · error · InvalidOperationException

Attributes may only be added immediately after frames of typ

Error message

Attributes may only be added immediately after frames of type Element or Component

What it means

Thrown by the private AssertCanAddAttribute guard invoked by AddAttribute/AddMultipleAttributes. Attributes are only valid immediately after an Element or Component frame (the last non-attribute frame must be Element or Component); text, markup, region, reference-capture, and named-event frames cannot receive attributes.

Source

Thrown at src/Components/Components/src/Rendering/RenderTreeBuilder.cs:724

        _lastNonAttributeFrameType = RenderTreeFrameType.Region;
    }

    /// <summary>
    /// Marks a previously appended region frame as closed. Calls to this method
    /// must be balanced with calls to <see cref="OpenRegion(int)"/>.
    /// </summary>
    public void CloseRegion()
    {
        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

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Emit all AddAttribute calls directly after OpenElement/OpenComponent and before any child content/text.
  2. If attributes are computed after content, restructure so attributes are added first or re-open the element/component.
  3. Check that no AddContent/AddMarkupContent/OpenRegion runs between the open and the attribute.

Example fix

// before
builder.OpenElement(0, "div");
builder.AddContent(1, "hello");
builder.AddAttribute(2, "class", "x");
builder.CloseElement();

// after
builder.OpenElement(0, "div");
builder.AddAttribute(1, "class", "x");
builder.AddContent(2, "hello");
builder.CloseElement();
Defensive patterns

Strategy: validation

Validate before calling

// Emit attributes immediately after OpenElement/OpenComponent and before any content frame.
void RenderDiv(RenderTreeBuilder b) {
    b.OpenElement(0, "div");
    b.AddAttribute(1, "class", _cls);   // attributes first
    b.AddContent(2, _text);            // then content
    b.CloseElement();
}

Prevention

When it happens

Trigger: Calling builder.AddAttribute(seq, name, value) after AddContent/AddMarkupContent/OpenRegion/AddElementReferenceCapture/AddNamedEvent — i.e., when _lastNonAttributeFrameType is not Element or Component. In .razor this maps to an attribute appearing after literal text or inside a region instead of directly on the tag.

Common situations: A misplaced attribute in hand-written RenderFragment after a text node; a conditional that emits text before the attribute; code generation that interleaves attributes with content frames.

Related errors


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/efbf81741734568b. Report an issue: GitHub.