dotnet/wpf · error · InvalidOperationException
Storyboard_UnableToFreeze
Storyboard_UnableToFreeze
Error message
SR.Storyboard_UnableToFreeze
What it means
BeginStoryboard.Seal throws InvalidOperationException (SR.Storyboard_UnableToFreeze) when the resolved Storyboard snapshot cannot be frozen. Sealing breaks thread affinity by freezing the storyboard; a storyboard holding unfreezable state (e.g. bound or dynamic values) prevents that.
Solutions
- Remove data bindings and DynamicResource references from the storyboard's animation values; use static values.
- Freeze any brushes, transforms, or keyframes used by the animations so the whole storyboard becomes freezable.
- Replace bound values with StaticResource references to pre-declared frozen resources.
Example fix
// before
<DoubleAnimation To="{Binding MaxOpacity}" Storyboard.TargetProperty="Opacity"/>
// after
<DoubleAnimation To="1.0" Storyboard.TargetProperty="Opacity"/> Defensive patterns
Strategy: validation
Validate before calling
if (storyboard.CanFreeze)
storyboard.Freeze();
else
throw new InvalidOperationException("Storyboard contains unfreezable values (bindings/dynamic resources)"); Try / catch
try { template.Seal(); }
catch (InvalidOperationException ex) { /* storyboard not freezable; strip bindings */ } Prevention
- Avoid {Binding} and {DynamicResource} inside storyboard animation values
- Freeze brushes, transforms, and keyframes used by animations
- Prefer static values or frozen StaticResource references in templates
When it happens
Trigger: Sealing a BeginStoryboard whose Storyboard's CanFreeze returns false - typically because the storyboard's animations contain data bindings, DynamicResource references, or other expressions that block freezing.
Common situations: Storyboards with animated property values set via {Binding} or {DynamicResource} inside styles/templates, or animations using non-frozen brushes/gradients with mutable state.
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
- CannotChangeAfterSealed
- CannotChangeAfterSealed
- IAnimatable_CantAnimateSealedDO
- SR.Format(SR.IAnimatable_CantAnimateSealedDO, dp…
- SR.Format(SR.Storyboard_ImmutableTargetNotSupported…
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/5bd8f4dc48c80ece.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Media/Animation/BeginStoryboard.cs:146
// Gets our Storyboard value. This value may have come from a
// ResourceReferenceExpression or might have been a deferred
// reference that has since been realized.
Storyboard snapshot = GetValue(StoryboardProperty) as Storyboard;
if( snapshot == null )
{
// This is the same error thrown by Begin if the Storyboard
// property couldn't be resolved at Begin time. Since we're
// not allowing changes after this point, lack of resolution
// here means the same thing.
throw new InvalidOperationException(SR.Storyboard_StoryboardReferenceRequired);
}
// We're planning to break our thread affinity - we also need to
// make sure the Storyboard can also be used accross threads.
if(!snapshot.CanFreeze)
{
throw new InvalidOperationException(SR.Storyboard_UnableToFreeze);
}
if(!snapshot.IsFrozen)
{
snapshot.Freeze();
}
// Promote that snapshot into a local value. This is a no-op if it
// was a deferred reference or local Storyboard, but if it came from a
// ResourceReferenceExpression it will replace the Expression object
// with a snapshot of its current value.
Storyboard = snapshot;
}
else
{
; // base.Seal() will throw exception for us if already sealed.
}
base.Seal(); View on GitHub (pinned to 81131a70a4)