dotnet/wpf · error · InvalidOperationException
SR.Format(SR.Storyboard_ImmutableTargetNotSupported…
Error message
SR.Format(SR.Storyboard_ImmutableTargetNotSupported, path.Path)
What it means
Thrown by ProcessComplexPath when a complex path resolved to a frozen (immutable) Freezable target and the library's clone-in-place strategy failed. WPF animates frozen Freezables by substituting a mutable clone into the tree; if the target property's value is not the clone after processing, the app is halted rather than corrupting the frozen object.
Solutions
- Do not freeze the Freezable being animated: remove PresentationOptions:Freeze or call GetAsFrozen less aggressively so the animation can target it.
- Create an unfrozen copy of the resource specifically for animation and target that instance.
- Simplify the property path so it targets a mutable DependencyObject property rather than traversing into frozen sub-objects.
- If the freeze is mandatory, animate a different mechanism (e.g. bind to a computed value or use a CompositionTarget.Rendering loop).
Example fix
// before <SolidColorBrush x:Key="animBrush" Color="Red" PresentationOptions:Freeze="True" /> // after <SolidColorBrush x:Key="animBrush" Color="Red" /> <!-- leave unfrozen so it can be cloned/animated -->
Defensive patterns
Strategy: validation
Validate before calling
var val = targetObject.GetValue(targetProperty) as Freezable;
if (val != null && val.IsFrozen)
{
var clone = val.CloneCurrentValue(); // animatable copy
} Type guard
bool CanAnimate(object o) => o is Freezable f ? !f.IsFrozen : o is DependencyObject;
Try / catch
try { storyboard.Begin(target, true); }
catch (InvalidOperationException ex) when (ex.Message.Contains("immutable")) { /* use an unfrozen clone as the animation source */ } Prevention
- Don't freeze Freezables you intend to animate
- Remove PresentationOptions:Freeze="True" on animated resources
- Clone frozen resources before targeting them in a storyboard
When it happens
Trigger: Storyboard with a complex PropertyPath that walks into a Freezable that is frozen (e.g. a Brush inside a frozen resource) and GetComplexPathValue cannot install a cloned, animatable instance at the expected property.
Common situations: Animating properties of shared resources defined with PresentationOptions:Freeze='True' or retrieved from a frozen cache; cross-thread shared brushes; deep property paths like 'Fill.(SolidColorBrush.Opacity)' on a frozen brush.
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
- SR.IAnimatable_CantAnimateSealedDO
- Animation_Invalid_DefaultValue
- IAnimatable_CantAnimateSealedDO
- SR.Animation_Invalid_DefaultValue
- SR.Animation_NoTextChildren
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/1bdf24b1b5179702.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Media/Animation/Storyboard.cs:874
// We need to clone the value of the target, and from here onwards
// try to pretend that it is the actual value.
Debug.Assert(targetPropertyValue is Freezable, "We shouldn't be trying to clone a type we don't understand. PropertyCloningRequired() has improperly flagged the current value as 'need to clone'.");
// To enable animations on frozen Freezable objects, complex
// path processing is done on a clone of the value.
Freezable clone = ((Freezable)targetPropertyValue).Clone();
SetComplexPathClone( targetObject, targetProperty, targetPropertyValue, clone );
// Promote the clone to the EffectiveValues cache
targetObject.InvalidateProperty(targetProperty);
// We're supposed to have the animatable clone in place by now. But if
// things went sour for whatever reason, halt the app instead of corrupting
// the frozen object.
if( targetObject.GetValue(targetProperty) != clone )
{
throw new InvalidOperationException(SR.Format(SR.Storyboard_ImmutableTargetNotSupported, path.Path));
}
// Now that we have a clone, update the animatedObject and animatedProperty
// with references to those on the clone.
using(path.SetContext(targetObject))
{
animatedObject = path.LastItem as DependencyObject;
animatedProperty = path.LastAccessor as DependencyProperty;
}
// And set up to listen to changes on this clone.
ChangeListener.ListenToChangesOnFreezable(
targetObject, clone, targetProperty, (Freezable)targetPropertyValue );
}
// Apply animation clock on the animated object/animated property.
ObjectPropertyPair directApplyTarget = new ObjectPropertyPair( animatedObject, animatedProperty );
UpdateMappings( clockMappings, directApplyTarget, animationClock );View on GitHub (pinned to 81131a70a4)