dotnet/wpf · critical · InvalidOperationException

SR.Format(SR.TemplateCircularReferenceFound, name…

Error message

SR.Format(SR.TemplateCircularReferenceFound, name, walkNode.GetType())

What it means

During template expansion, the walk up the chain of Style/Template-generated containers found an element of the same type with the same style as the container, meaning the template re-applies itself and would recurse infinitely. WPF throws InvalidOperationException (TemplateCircularReferenceFound) naming the element and type.

Solutions

  1. Change the inner style to target a different element type or remove the template from it
  2. Use BasedOn and override the inner root's Template to a distinct, non-recursive template
  3. Apply the inner style via explicit key (x:Key) rather than the implicit type style
  4. Break the chain by not styling the template's root element with the same style as the templated parent

Example fix

<!-- before: implicit style reapplies same template to the template root -->
<Style TargetType="ListBoxItem"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="ListBoxItem"><Border Style="{StaticResource listBoxItemStyle}"/></ControlTemplate></Setter.Value></Setter></Style>
// after: use a keyed style for the root that does not set the same Template
<Style x:Key="itemRoot" TargetType="Border"><Setter Property="Background" Value="Transparent"/></Style>
Defensive patterns

Strategy: validation

Validate before calling

// Before applying, ensure the template's root does not carry the same implicit style
var rootStyle = (Style)root.FindResource(root.GetType());
if (rootStyle != null && Equals(rootStyle, containerStyle))
    throw new InvalidOperationException("Template self-reference detected");

Try / catch

try { control.Template = tpl; } catch (InvalidOperationException ex) when (ex.Message.Contains("Circular")) { Log("Template cycle for " + control.GetType().Name); control.Template = null; }

Prevention

When it happens

Trigger: A ControlTemplate whose root element uses a Style that targets the same control type and applies the same template back; DefaultStyleKey/Style re-assignment creating a self-referencing template chain.

Common situations: Restyling a templated control's root with a style keyed by the same type (implicit style) that includes the same ControlTemplate; accidentally omitting a TemplateBinding to break the loop; copying a template into a style applied to its own root.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/f91944c7ab451e9e. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/StyleHelper.cs:2079

                {
                    nextParent = fceWalkNode.TemplatedParent;
                }

                // If we're beyond "this" container, check for identical Style & type
                //  because that indicates a cycle.  If so, stop the train.
                if( walkNode != container && nextParent != null ) // Only interested in nodes that are "Not me" and not auto-generated (== no TemplatedParent)
                {
                    // Do the cheaper comparison first - the Style reference should be cached
                    if ((frameworkTemplate != null && walkNodeIsFE && feWalkNode.TemplateInternal == frameworkTemplate) )
                    {
                        // Then the expensive one - pulling in reflection to check if they're also the same types.
                        if( walkNode.GetType() == container.GetType() )
                        {
                            string name = (walkNodeIsFE) ? feWalkNode.Name : fceWalkNode.Name;

                            // Same Style, Same type, on a chain of Style-created nodes.
                            //  This is bad news since this chain will continue indefinitely.
                            throw new InvalidOperationException(
                                SR.Format(SR.TemplateCircularReferenceFound, name, walkNode.GetType()));
                        }
                    }
                }

                // If the container is, in turn, created from another Style,
                //  keep walking up that chain.  Exception:  do not walk up from a
                //  ContentPresenter;  this avoids false positives involving a
                //  ContentControl whose effective ContentTemplate contains another
                //  instance of the same type of ContentControl, for example:
                //          <Button Content="A">
                //              <Button.ContentTemplate>
                //                  <DataTemplate>
                //                      <Button Content="B"/>
                //                  </DataTemplate>
                //              </Button.ContentTemplate>
                //          </Button>
                //  Both Buttons have the same ControlTemplate, which would be flagged

View on GitHub (pinned to 81131a70a4)