dotnet/wpf · error · InvalidOperationException
SR.TriggerActionAlreadySealed
Error message
SR.TriggerActionAlreadySealed
What it means
TriggerAction.Seal() throws InvalidOperationException with SR.TriggerActionAlreadySealed when Seal() is invoked on a TriggerAction that is already sealed. WPF seals triggers/actions when the owning Style or Template is sealed (made immutable), typically when it is first used by an element. The check is an internal invariant guard against double-sealing.
Solutions
- Do not call Seal() explicitly on styles/triggers; let WPF seal them when the style is applied
- If reusing trigger objects, create a new TriggerAction instance per Style instead of sharing one
- Check IsSealed before any manual Seal()/mutation and skip or clone if already sealed
Example fix
// before
if (!myAction.IsSealed) { /* mutate */ }
myAction.Seal(); // may already be sealed
// after
if (myAction.IsSealed)
{
myAction = (TriggerAction)CloneCurrentValue(); // work on a fresh copy
}
else { /* mutate */ myAction.Seal(); } Defensive patterns
Strategy: validation
Validate before calling
if (!action.IsSealed) { action.Seal(); } Type guard
static bool CanSeal(TriggerAction a) => a != null && !a.IsSealed;
Prevention
- Never call Seal() manually; let WPF seal styles on application
- Don't share TriggerAction instances across styles
- Check IsSealed before any manual lifecycle calls
When it happens
Trigger: Calling the internal Seal() method on a TriggerAction instance whose IsSealed is already true — i.e., sealing the same action twice, usually via two Seal() passes over the same style/template graph.
Common situations: Reusing one shared TriggerAction/Style object across multiple styles or templates that each trigger sealing; custom framework code that calls Seal() manually on a style already in use.
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.CannotChangeAfterSealed, "trigger")
- SR.Format(SR.CannotChangeAfterSealed, "TriggerAction")
- SR.Format(SR.CannotChangeAfterSealed…
- SR.Format(SR.IAnimatable_CantAnimateSealedDO, dp…
- Animation_Invalid_DefaultValue
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/0a84dfd24c4e437f.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/TriggerAction.cs:102
{
if( IsSealed && containingTrigger != _containingTrigger )
{
throw new InvalidOperationException(SR.TriggerActionMustBelongToASingleTrigger);
}
_containingTrigger = containingTrigger;
Seal();
}
/// <summary>
/// A derived class overrideing Seal() should set object state such
/// that further changes are not allowed. This is also a time to make
/// validation checks to see if all parameters make sense.
/// </summary>
internal override void Seal()
{
if( IsSealed )
{
throw new InvalidOperationException(SR.TriggerActionAlreadySealed);
}
base.Seal();
}
/// <summary>
/// Checks sealed status and throws exception if object is sealed
/// </summary>
internal void CheckSealed()
{
if( IsSealed )
{
throw new InvalidOperationException(SR.Format(SR.CannotChangeAfterSealed, "TriggerAction"));
}
}
// Define the DO's inheritance context
internal override DependencyObject InheritanceContextView on GitHub (pinned to 81131a70a4)