dotnet/wpf · error · InvalidOperationException

SR.PropertyTriggerLayerLimitExceeded

Error message

SR.PropertyTriggerLayerLimitExceeded

What it means

TriggerBase.EstablishLayer assigns each property/data trigger a global layer rank used to order trigger evaluation in styles. WPF throws this InvalidOperationException when the global layer rank counter reaches Int64.MaxValue, meaning an application has created an astronomically large number of trigger layers. This is effectively an internal invariant violation and practically unreachable in normal apps.

Solutions

  1. Restart the process; the global layer rank counter cannot be reset
  2. Reduce dynamic style/trigger churn by reusing styles and templates instead of recreating them
  3. Fix any leak where styles or triggers are created in a loop without releasing them
  4. Report to Microsoft if reached without extreme trigger churn, as it indicates counter corruption

Example fix

// before
for (int i = 0; i < int.MaxValue; i++) {
    var t = new Trigger { Property = UIElement.IsEnabledProperty };
    var s = new Style { Triggers = { t } }; // churns trigger layers
}
// after
var sharedTrigger = new Trigger { Property = UIElement.IsEnabledProperty };
var sharedStyle = new Style { Triggers = { sharedTrigger } }; // reuse, do not recreate
for (int i = 0; i < int.MaxValue; i++) { /* apply sharedStyle */ }
Defensive patterns

Strategy: validation

Validate before calling

if (style.Triggers.Count > 1000) { throw new InvalidOperationException("Excessive trigger churn detected"); }

Try / catch

try { style.Triggers.Add(trigger); } catch (InvalidOperationException ex) when (ex.Message.Contains("layer")) { /* log and restart/rebuild styles */ }

Prevention

When it happens

Trigger: Calling Style.Triggers.Add (which routes through AddPropertyTriggerWithAction or AddDataTriggerWithAction) after the process has accumulated Int64.MaxValue trigger layer ranks - only possible with pathological repeated style/trigger instantiation and removal.

Common situations: Essentially never seen; would require a long-running process leaking triggers for billions of iterations, or memory corruption affecting _nextGlobalLayerRank.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/TriggerBase.cs:358

        //  animation composition.  Otherwise, it stays at default value of zero.
        internal Int64 Layer
        {
            get { return _globalLayerRank; }
        }

        // Set self rank to current number, increment global static.
        internal void EstablishLayer()
        {
            if( _globalLayerRank == 0 )
            {
                lock(Synchronized)
                {
                    _globalLayerRank = _nextGlobalLayerRank++;
                }

                if( _nextGlobalLayerRank == Int64.MaxValue )
                {
                    throw new InvalidOperationException(SR.PropertyTriggerLayerLimitExceeded);
                }
            }
        }

        // evaluate the current state of the trigger
        internal virtual bool GetCurrentState(DependencyObject container, UncommonField<HybridDictionary[]> dataField)
        {
            Debug.Assert( false,
                "This method was written to handle Trigger, MultiTrigger, DataTrigger, and MultiDataTrigger.  It looks like a new trigger type was added - please add support as appropriate.");

            return false;
        }

        // Collection of TriggerConditions
        internal TriggerCondition[] TriggerConditions
        {
            get { return _triggerConditions; }
            set { _triggerConditions = value; }

View on GitHub (pinned to 81131a70a4)