dotnet/wpf · error · InvalidOperationException

SR.Format(SR.CyclicThemeStyleReferenceDetected, this)

Error message

SR.Format(SR.CyclicThemeStyleReferenceDetected, this)

What it means

FrameworkElement.UpdateThemeStyleProperty detects re-entrancy of the theme style update: it is invoked (via OnInitialized or OnResourcesChanged) while IsThemeStyleUpdateInProgress is already true, meaning the theme style resolution cycled back to the same element. WPF throws InvalidOperationException (CyclicThemeStyleReferenceDetected) to break the recursion.

Solutions

  1. Break the resource cycle: ensure the theme style's resource references do not point back to resources whose change re-invalidates the element's theme style.
  2. Defer resource/style changes in OnThemeChanged/OnResourcesChanged overrides with Dispatcher.BeginInvoke.
  3. Keep ThemeStyle/DefaultStyleKey metadata static; don't change theme-related keys during style application.
  4. The exception message names the offending element — inspect its theme style and referenced dictionaries.

Example fix

// before
protected override void OnThemeChanged()
{
    SetResourceReference(StyleProperty, ThemeKey); // re-invalidates theme style mid-update
}

// after
protected override void OnThemeChanged()
{
    Dispatcher.BeginInvoke(() => SetResourceReference(StyleProperty, ThemeKey));
}
Defensive patterns

Strategy: validation

Validate before calling

if (element.IsThemeStyleUpdateInProgress)
    throw new InvalidOperationException("Theme style update already in progress; defer resource changes.");

Type guard

bool IsSafeForThemeStyleUpdate(FrameworkElement e) => !e.IsThemeStyleUpdateInProgress;

Try / catch

try
{
    element.InvalidateProperty(ThemeStyleProperty);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("cyclic"))
{
    Log.Warn("Cyclic theme style reference detected.", ex);
}

Prevention

When it happens

Trigger: Theme style lookup for the element triggers OnResourcesChanged or re-initialization on the same element before the current theme style update finishes — e.g. resource references inside the theme style that invalidate the element's theme style again, or overrides that re-fetch theme style during the update.

Common situations: Custom control theme dictionaries with DynamicResource keys that change during style application; overriding OnThemeChanged/OnResourcesChanged to re-apply the theme style; resource dictionary churn while a theme style is being applied (e.g. theme switch handlers).

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Generated/FrameworkElement.cs:679

                    if (toolTip != null)
                    {
                        FrameworkObject toolTipFO = new FrameworkObject(toolTip);
                        if (toolTipFO.IsValid)
                        {
                            TreeWalkHelper.InvalidateOnResourcesChange(toolTipFO.FE, toolTipFO.FCE, ResourcesChangeInfo.ThemeChangeInfo);
                        }
                    }

                    OnThemeChanged();
                }
                finally
                {
                    IsThemeStyleUpdateInProgress = false;
                }
            }
            else
            {
                throw new InvalidOperationException(SR.Format(SR.CyclicThemeStyleReferenceDetected, this));
            }
        }

        // Called when the theme changes so resources not in the tree can be updated by subclasses
        internal virtual void OnThemeChanged()
        {
        }

        ///<summary>
        ///     Initiate the processing for Loaded event broadcast starting at this node
        /// </summary>
        /// <remarks>
        ///     This method is to allow firing Loaded event from a Helper class since the override is protected
        /// </remarks>
        internal void FireLoadedOnDescendentsInternal()
        {
            // This is to prevent duplicate Broadcasts for the Loaded event
            if (LoadedPending == null)

View on GitHub (pinned to 81131a70a4)