dotnet/wpf · error · InvalidOperationException

SR.Format(SR.IAnimatable_CantAnimateSealedDO, dp…

Error message

SR.Format(SR.IAnimatable_CantAnimateSealedDO, dp, this.GetType())

What it means

UIElement.ApplyAnimationClock throws InvalidOperationException(SR.IAnimatable_CantAnimateSealedDO, formatted with the DP and element type) when IsSealed is true. A sealed (frozen/immutable) DependencyObject cannot have its animation state modified, so the library refuses the operation.

Solutions

  1. Animate a non-frozen surrogate: put the brush on the element and animate the element property, or use Clone() to get an unfrozen copy.
  2. Do not Freeze() objects you intend to animate later.
  3. Check freezable.CanFreeze / IsFrozen before deciding to freeze; keep animatable instances unfrozen.

Example fix

// before
brush.Freeze();
brush.ApplyAnimationClock(SolidColorBrush.ColorProperty, clock); // sealed -> throws
// after
var animatableBrush = brush.Clone();
animatableBrush.ApplyAnimationClock(SolidColorBrush.ColorProperty, clock);
Defensive patterns

Strategy: type-guard

Validate before calling

var f = element as Freezable;
if (f != null && f.IsFrozen) { element = f.Clone(); } // animate the unfrozen clone

Type guard

bool CanBeAnimated(DependencyObject o) => o is not Freezable f || !f.IsFrozen;

Try / catch

try { element.ApplyAnimationClock(dp, clock); }
catch (InvalidOperationException) { var clone = ((Freezable)element).Clone(); clone.ApplyAnimationClock(dp, clock); }

Prevention

When it happens

Trigger: Calling ApplyAnimationClock on a freezable element (e.g. a Brush, Pen, or Geometry) after Freeze() was called or it was sealed by the framework (e.g. used in a resource that got frozen).

Common situations: Trying to animate a frozen Brush in a style; animating a shared frozen resource; calling Freeze() earlier in code then later attempting animation.

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/d6e31482e1e75340. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Generated/UIElement.cs:87

            if (!AnimationStorage.IsPropertyAnimatable(this, dp))
            {
                throw new ArgumentException(SR.Format(SR.Animation_DependencyPropertyIsNotAnimatable, dp.Name, this.GetType()), nameof(dp));
            }

            if (clock != null
                && !AnimationStorage.IsAnimationValid(dp, clock.Timeline))
            {
                throw new ArgumentException(SR.Format(SR.Animation_AnimationTimelineTypeMismatch, clock.Timeline.GetType(), dp.Name, dp.PropertyType), nameof(clock));
            }

            if (!HandoffBehaviorEnum.IsDefined(handoffBehavior))
            {
                throw new ArgumentException(SR.Animation_UnrecognizedHandoffBehavior);
            }

            if (IsSealed)
            {
                throw new InvalidOperationException(SR.Format(SR.IAnimatable_CantAnimateSealedDO, dp, this.GetType()));
            }

            AnimationStorage.ApplyAnimationClock(this, dp, clock, handoffBehavior);
        }

        /// <summary>
        /// Starts an animation for a DependencyProperty. The animation will
        /// begin when the next frame is rendered.
        /// </summary>
        /// <param name="dp">
        /// The DependencyProperty to animate.
        /// </param>
        /// <param name="animation">
        /// <para>The AnimationTimeline to used to animate the property.</para>
        /// <para>If the AnimationTimeline's BeginTime is null, any current animations
        /// will be removed and the current value of the property will be held.</para>
        /// <para>If this value is null, all animations will be removed from the property
        /// and the property value will revert back to its base value.</para>

View on GitHub (pinned to 81131a70a4)