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
- Restart the process; the global layer rank counter cannot be reset
- Reduce dynamic style/trigger churn by reusing styles and templates instead of recreating them
- Fix any leak where styles or triggers are created in a loop without releasing them
- 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
- Reuse style/trigger instances instead of recreating them in loops
- Pool or cache DynamicResource-driven styles for long-running apps
- Avoid unbounded dynamic trigger creation
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
- SR.MissingTriggerProperty
- SR.CannotChangeAfterSealed
- SR.ConditionCannotUseBothPropertyAndBinding
- SR.ConditionValueOfExpressionNotSupported
- SR.ConditionValueOfMarkupExtensionNotSupported
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)