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

  1. Do not call Seal() explicitly on styles/triggers; let WPF seal them when the style is applied
  2. If reusing trigger objects, create a new TriggerAction instance per Style instead of sharing one
  3. 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

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


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 InheritanceContext

View on GitHub (pinned to 81131a70a4)