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
- Change the inner style to target a different element type or remove the template from it
- Use BasedOn and override the inner root's Template to a distinct, non-recursive template
- Apply the inner style via explicit key (x:Key) rather than the implicit type style
- 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
- Never style the template's root element with a style that sets the same template
- Use x:Key styles for inner elements instead of implicit type styles
- Draw the style/template chain on paper for custom control themes
- Keep inner root templates distinct via BasedOn overrides
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
- SR.Format(SR.PropertyTriggerCycleDetected, source.Name)
- SR.ChildTemplateInstanceDoesNotExist
- SR.CyclicThemeStyleReferenceDetected
- SR.ElementMustBelongToTemplate
- SR.Format(SR.CannotChangeAfterSealed…
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 flaggedView on GitHub (pinned to 81131a70a4)