dotnet/wpf · error · InvalidOperationException

Storyboard_PropertyPathFrozenCheckFailed

Storyboard_PropertyPathFrozenCheckFailed

Error message

SR.Format(SR.Storyboard_PropertyPathFrozenCheckFailed, AccessorName(path, i-1), path.Path, intermediateFreezable.GetType().ToString())

What it means

An intermediate object along the animation property path is a frozen (immutable) Freezable, so Storyboard cannot traverse or modify through it. VerifyPathIsAnimatable throws this InvalidOperationException naming the accessor, path, and the frozen type.

Solutions

  1. Clone the frozen Freezable via CloneCurrentValue and target the unfrozen copy (e.g. assign a mutable brush to the element first).
  2. Avoid calling Freeze() on objects whose sub-properties you animate.
  3. Change the target to an unfrozen equivalent resource.
  4. Assign a new live instance (element.Fill = new SolidColorBrush(...)) and animate that instead.
  5. Restructure the path so it does not pass through the frozen object.

Example fix

// before
brush.Freeze();
element.Fill = brush;
storyboard.Begin(element); // animates (SolidColorBrush.Color)
// after
var animatableBrush = brush.CloneCurrentValue();
element.Fill = animatableBrush; // keep unfrozen
storyboard.Begin(element);
Defensive patterns

Strategy: validation

Validate before calling

static bool PathObjectsUnfrozen(PropertyPath path, object root)
{
    path.Evaluate(root);
    for (int i = 0; i < path.Get getItemCount; i++)
    {
        if (path.GetItem(i) is Freezable f && f.IsFrozen) return false;
    }
    return true;
}

Type guard

bool IsAnimatableFreezable(object o) => !(o is Freezable f && f.IsFrozen);

Try / catch

try { storyboard.Begin(element); }
catch (InvalidOperationException ex) when (ex.Message.Contains("frozen") || ex.Message.Contains("Frozen"))
{
    // clone the frozen object and retry with mutable instance
}

Prevention

When it happens

Trigger: A path traverses a Freezable that was Freeze()d or auto-frozen (e.g. a frozen Brush or Geometry) and the animation needs to set a sub-property through it, with segment index i > 0.

Common situations: Animating (SolidColorBrush.Color) on a brush that was frozen for performance or declared in a resource dictionary that froze it; frozen Geometry/Transform collections in paths; freezing objects after cross-thread use then animating their sub-properties.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Media/Animation/Storyboard.cs:724

            //  Object 0 is the Button, object 1 is the brush.
            if( i == 1 )
            {
                intermediateFreezable = intermediateObject as Freezable;
                if( intermediateFreezable != null && intermediateFreezable.IsFrozen )
                {
                    checkingFrozenState = false;
                }
            }
            // Freezable objects (other than the one returned as the value of
            //  the first property) must not be frozen if the first one isn't.
            else if( checkingFrozenState )
            {
                intermediateFreezable = intermediateObject as Freezable;
                if( intermediateFreezable != null && intermediateFreezable.IsFrozen )
                {
                    if( i > 0 )
                    {
                        throw new InvalidOperationException(SR.Format(SR.Storyboard_PropertyPathFrozenCheckFailed, AccessorName(path, i-1), path.Path, intermediateFreezable.GetType().ToString() ));
                    }
                    else
                    {
                        // i == 0 means the targeted object itself is a frozen Freezable.
                        //  This need a different error message.
                        throw new InvalidOperationException(SR.Format(SR.Storyboard_ImmutableTargetNotSupported, path.Path));
                    }
                }
            }

            // The last object + property pairing (the one we're actually going
            //  to stick the clock on) has further requirements.
            if( i == path.Length-1 )
            {
                DependencyObject intermediateDO = intermediateObject as DependencyObject;
                DependencyProperty intermediateDP = intermediateProperty as DependencyProperty;

                if( intermediateDO == null )

View on GitHub (pinned to 81131a70a4)