dotnet/aspnetcore · error · InvalidOperationException

Cannot set a key outside the scope of a component or element

Error message

Cannot set a key outside the scope of a component or element.

What it means

RenderTreeBuilder.SetKey assigns a @key to the current open element or component. It locates the current parent frame index via GetCurrentParentFrameIndex(); if there is no open element or component (the frame stack is empty or the current scope is not an element/component), it throws InvalidOperationException. Keys can only be set within the scope of an element or component frame.

Source

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

    }

    /// <summary>
    /// Assigns the specified key value to the current element or component.
    /// </summary>
    /// <param name="value">The value for the key.</param>
    public void SetKey(object? value)
    {
        if (value == null)
        {
            // Null is equivalent to not having set a key, which is valuable because Razor syntax doesn't have an
            // easy way to have conditional directive attributes
            return;
        }

        var parentFrameIndex = GetCurrentParentFrameIndex();
        if (!parentFrameIndex.HasValue)
        {
            throw new InvalidOperationException("Cannot set a key outside the scope of a component or element.");
        }

        var parentFrameIndexValue = parentFrameIndex.Value;
        ref var parentFrame = ref _entries.Buffer[parentFrameIndexValue];
        switch (parentFrame.FrameTypeField)
        {
            case RenderTreeFrameType.Element:
                parentFrame.ElementKeyField = value; // It's a ref var, so this writes to the array
                break;
            case RenderTreeFrameType.Component:
                parentFrame.ComponentKeyField = value; // It's a ref var, so this writes to the array
                break;
            default:
                throw new InvalidOperationException($"Cannot set a key on a frame of type {parentFrame.FrameTypeField}.");
        }
    }

    private void OpenComponentUnchecked(int sequence, [DynamicallyAccessedMembers(Component)] Type componentType)

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Ensure SetKey is called after OpenElement/OpenComponent and before CloseElement/CloseComponent.
  2. In Razor, place @key as an attribute on an element or component, not as a standalone directive.
  3. Check the call ordering: OpenElement -> ... -> SetKey -> CloseElement.
  4. Remove the @key directive if it's not attached to a specific element or component.

Example fix

<!-- before (Razor) -->
@key("myKey")
<div>Hello</div>

<!-- after -->
<div @key="myKey">Hello</div>
Defensive patterns

Strategy: validation

Validate before calling

// In Razor, ensure @key is on an element or component
// In manual code, ensure OpenElement/OpenComponent precedes SetKey
builder.OpenElement(0, "div");
builder.SetKey("myKey");
builder.CloseElement();

Prevention

When it happens

Trigger: Calling builder.SetKey(myKey) when no element or component is currently open (i.e., at the top level of the builder, or after CloseElement/CloseComponent without a new Open). This corresponds to placing @key outside of any element or component in Razor markup.

Common situations: Placing @key before an element/component tag in Razor; calling SetKey at the top level of a render fragment; calling SetKey after closing the last frame; generated code that emits SetKey in the wrong position.

Related errors


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