{"record":{"id":"61203deca54eb516","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 'Attribute'.","messagePattern":"The frame at index (.+?) is of type '(.+?)', not 'Attribute'\\.","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/294cab2f9b2e03af6b953820c7ab497c3c8b7ad9/src/Components/Components/src/Rendering/RenderTreeBuilder.cs#L785-L821","documentation":"Thrown by RenderTreeBuilder.SetAttributeValue (the experimental API for mutating an already-appended attribute frame in place). It checks the frame at frameIndex is of type Attribute; SetAttributeValue only updates attribute values, so pointing it at an element/component/text frame is rejected.","triggerScenarios":"Calling builder.SetAttributeValue(frameIndex, value) with an index that refers to a non-Attribute frame (element, component, text, region, etc.). Typically an off-by-one or a stale index captured before re-appending frames.","commonSituations":"Wrapping a RenderFragment and caching a frame index, then the index drifts after the fragment re-renders or after InsertAttributeExpensive shifts frames; miscounting sequence vs. physical frame index.","solutions":["Recompute the target frame index from the current frames array before calling SetAttributeValue.","Confirm the frame at that index is an Attribute frame (filter by RenderTreeFrameType.Attribute) before mutating.","If frames may have shifted, re-scan by attribute name instead of trusting a cached index."],"exampleFix":"// before\nvar idx = cachedIndex; // possibly stale\nbuilder.SetAttributeValue(idx, \"new\");\n\n// after\nvar frames = builder.GetFrames();\nint idx = -1;\nfor (int i = 0; i < frames.Count; i++)\n    if (frames.Array[i].FrameTypeField == RenderTreeFrameType.Attribute\n        && frames.Array[i].AttributeNameField == \"value\") { idx = i; break; }\nif (idx >= 0) builder.SetAttributeValue(idx, \"new\");","handlingStrategy":"validation","validationCode":"// Resolve and type-check the frame index immediately before mutating.\nvar frames = builder.GetFrames();\nif ((uint)frameIndex < (uint)frames.Count\n    && frames.Array[frameIndex].FrameTypeField == RenderTreeFrameType.Attribute)\n{\n    builder.SetAttributeValue(frameIndex, value);\n}","typeGuard":"static bool IsAttributeFrame(in RenderTreeFrame f)\n    => f.FrameTypeField == RenderTreeFrameType.Attribute;","tryCatchPattern":null,"preventionTips":["Treat SetAttributeValue as experimental (ASP0032) and prefer full re-render over in-place mutation.","Never cache a frame index across renders — re-derive it from the current frames each time.","Scope-mutation: scan by attribute name rather than trusting a stale index."],"tags":["blazor","rendering","rendertree","setattributevalue","experimental"],"analyzedSha":"294cab2f9b2e03af6b953820c7ab497c3c8b7ad9","analyzedAt":"2026-08-06T20:08:02.189Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}