{"record":{"id":"c44abec69ebf2b81","repo":"dotnet/aspnetcore","slug":"the-frame-at-index-frameindex-is-of-type-frame","errorCode":null,"errorMessage":"The frame at index {frameIndex} is of type '{frame.FrameTypeField}', not '{RenderTreeFrameType.Attribute}'.","messagePattern":"The frame at index (.+?) is of type '(.+?)', not '(.+?)'\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Components/Components/src/Rendering/RenderTreeBuilder.cs","lineNumber":803,"sourceCode":"    /// <param name=\"frameIndex\">The zero-based index of the attribute frame whose value should be replaced.</param>\n    /// <param name=\"value\">The new attribute value.</param>\n    /// <exception cref=\"ArgumentOutOfRangeException\">Thrown when <paramref name=\"frameIndex\"/> is outside the range of appended frames.</exception>\n    /// <exception cref=\"InvalidOperationException\">Thrown when the frame at <paramref name=\"frameIndex\"/> is not of type <see cref=\"RenderTreeFrameType.Attribute\"/>.</exception>\n    [Experimental(\"ASP0032\", UrlFormat = \"https://aka.ms/aspnet/analyzer/{0}\")]\n    public void SetAttributeValue(int frameIndex, object? value)\n    {\n        var frames = _entries.Buffer;\n        var count = _entries.Count;\n\n        if ((uint)frameIndex >= (uint)count)\n        {\n            throw new ArgumentOutOfRangeException(nameof(frameIndex));\n        }\n\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","sourceCodeStart":785,"sourceCodeEnd":821,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/Components/Components/src/Rendering/RenderTreeBuilder.cs#L785-L821","documentation":"RenderTreeBuilder's internal frame mutation API (the one that updates an attribute value by index) validates that the targeted frame is actually an Attribute frame. The render buffer is a heterogeneous array where element frames, component frames, attribute frames, and text frames are interleaved; writing an attribute value slot into a frame that holds, say, an Element or Text would silently corrupt the tree. The guard throws InvalidOperationException naming the mismatched frame type so the corruption is caught at the source.","triggerScenarios":"Calling the internal SetAttributeValue(int frameIndex, ...) overload (or the diff/edits APIs that index into _entries.Buffer) with an index that resolves to a non-Attribute frame. Typically arises in custom renderers, tree-diffing code, or reflection-based tree rewriting that computed the index from the wrong frame scan.","commonSituations":"Writing a custom RenderTreeBuilder manipulation outside the supported public API; upgrading across Blazor versions where frame layout changed and stale frame indices are cached; Hot-reload or component-diff tooling that holds indices across renders; tests that poke the builder's internal buffer directly.","solutions":["Do not index into the builder's frames directly — re-derive the attribute frame index by scanning only Attribute frames after the owning element/component open.","If you must mutate, use RenderTreeFrame.ElementAtOrDefault / frame iteration helpers that check FrameTypeField == Attribute before writing.","Upgrade any code caching frame indices across renders to recompute them every render.","Prefer the public AddAttribute/AddComponentParameter path over internal SetAttributeValue unless you are the renderer."],"exampleFix":"// before (unsafe index reuse)\nvar idx = cachedAttributeIndex;\nbuilder.SetAttributeValue(idx, newValue); // throws if idx now points at a non-attribute frame\n// after\n// re-scan for the attribute frame owned by the current element\nref var frame = ref FindAttributeFrame(builder, elementOpenIndex);\nif (frame.FrameTypeField == RenderTreeFrameType.Attribute) {\n    frame.AttributeValueField = newValue;\n}","handlingStrategy":"validation","validationCode":"// When indexing into the render buffer to mutate an attribute, validate the frame first.\nref var frame = ref frames[frameIndex];\nif (frame.FrameTypeField != RenderTreeFrameType.Attribute)\n    throw new InvalidOperationException($\"Refusing to write: frame {frameIndex} is {frame.FrameTypeField}\");\nframe.AttributeValueField = value;","typeGuard":"static bool IsAttributeFrame(in RenderTreeFrame frame)\n    => frame.FrameTypeField == RenderTreeFrameType.Attribute;","tryCatchPattern":null,"preventionTips":["Avoid indexing into RenderTreeBuilder frames directly; use the public AddAttribute / SetAttributeValue APIs.","Never cache frame indices across renders — frame layout can change.","If you build a custom renderer, scan-and-validate before mutating any frame.","Treat internal RenderTreeBuilder members as unstable across versions."],"tags":["blazor","render-tree","renderframe","invalidoperationexception"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}