dotnet/wpf · error · InvalidOperationException

Storyboard_ImmutableTargetNotSupported

Storyboard_ImmutableTargetNotSupported

Error message

SR.Format(SR.Storyboard_ImmutableTargetNotSupported, path.Path)

What it means

The root target object of the animation property path itself is a frozen Freezable (path segment index 0), which cannot be animated. VerifyPathIsAnimatable throws this InvalidOperationException with the path — a distinct message from the intermediate-segment frozen error because nothing along the path can ever be mutable.

Solutions

  1. Target a live DependencyObject (e.g. the element), animating its property rather than the frozen object itself.
  2. Use CloneCurrentValue to obtain a mutable copy and register/apply the animation on that copy.
  3. Do not Freeze() objects intended as animation targets.
  4. Move the animation to the element property that owns the frozen value.
  5. Re-register the unfrozen instance under the same name before Begin.

Example fix

// before
var brush = (SolidColorBrush)FindResource("sharedBrush");
storyboard.Begin(brush); // brush is frozen
// after
var brush = new SolidColorBrush(((SolidColorBrush)FindResource("sharedBrush")).Color);
element.Fill = brush;
storyboard.Begin(element);
Defensive patterns

Strategy: validation

Validate before calling

bool CanTarget(object o) => o is Freezable f ? !f.IsFrozen : o is DependencyObject;

Type guard

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

Try / catch

try { storyboard.Begin(targetObject); }
catch (InvalidOperationException ex) when (ex.Message.Contains("immutable") || ex.Message.Contains("frozen"))
{
    // resolve to a mutable clone or re-target a live DependencyObject
}

Prevention

When it happens

Trigger: Storyboard.TargetName (or Target) resolves to an object that has been frozen — e.g. a Freezable registered as a resource, or a storyboard applied to a shared frozen resource instance rather than a UI element.

Common situations: Targeting a shared frozen Brush/Geometry resource directly; freezing a Freezable then using it as the animation target via RegisterName; static resources auto-frozen in some contexts then used as animation targets.

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

Appendix: source

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

                    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 )
                {
                    Debug.Assert( i > 0, "The caller should not have set the PropertyPath context to a non DependencyObject." );
                    throw new InvalidOperationException(SR.Format(SR.Storyboard_PropertyPathMustPointToDependencyObject, AccessorName(path, i-1), path.Path));
                }

                if( intermediateDP == null )

View on GitHub (pinned to 81131a70a4)