dotnet/wpf · error · InvalidOperationException
CannotChangeAfterSealed
CannotChangeAfterSealed
Error message
SR.Format(SR.CannotChangeAfterSealed, "SetStoryboardSpeedRatio")
What it means
SetStoryboardSpeedRatio.SpeedRatio's setter throws InvalidOperationException when the action object is sealed. Like other controllable storyboard actions, it becomes read-only once attached to a trigger/storyboard and put into use.
Solutions
- Create a new SetStoryboardSpeedRatio with the desired SpeedRatio for each change.
- Set SpeedRatio before the action is attached to any EventTrigger or storyboard.
- Alternatively call Storyboard.SetSpeedRatio directly on the target's clock.
Example fix
// before
sharedSpeedAction.SpeedRatio = 2.0; // throws once sealed
// after
var action = new SetStoryboardSpeedRatio { Storyboard = targetStoryboard, SpeedRatio = 2.0 }; Defensive patterns
Strategy: type-guard
Validate before calling
if (action.IsSealed) action = new SetStoryboardSpeedRatio { Storyboard = action.Storyboard, SpeedRatio = newRatio }; Type guard
static SetStoryboardSpeedRatio EnsureMutable(SetStoryboardSpeedRatio a, double ratio) => a.IsSealed ? new SetStoryboardSpeedRatio { Storyboard = a.Storyboard, SpeedRatio = ratio } : a; Try / catch
try { action.SpeedRatio = ratio; } catch (InvalidOperationException) { action = new SetStoryboardSpeedRatio { /* reconfigure */ }; } Prevention
- Create a new SetStoryboardSpeedRatio per speed change
- Configure properties before attaching to triggers
- Use Storyboard.SetSpeedRatio for runtime changes
When it happens
Trigger: Setting SpeedRatio on a SetStoryboardSpeedRatio instance after it has been sealed — e.g. mutating a shared or template-declared action at runtime.
Common situations: Dynamically updating the speed ratio by reusing one SetStoryboardSpeedRatio object instead of creating a new one per trigger invocation.
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
- CannotChangeAfterSealed
- SR.Format(SR.IAnimatable_CantAnimateSealedDO, dp…
- SR.Format(SR.Storyboard_AnimationMismatch…
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/45e75c5620142f76.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Media/Animation/SetStoryboardSpeedRatio.cs:37
/// </summary>
public sealed class SetStoryboardSpeedRatio : ControllableStoryboardAction
{
/// <summary>
/// A speed ratio to use for this action. If it is never explicitly
/// specified, it is 1.0.
/// </summary>
[DefaultValue(1.0)]
public double SpeedRatio
{
get
{
return _speedRatio;
}
set
{
if (IsSealed)
{
throw new InvalidOperationException(SR.Format(SR.CannotChangeAfterSealed, "SetStoryboardSpeedRatio"));
}
_speedRatio = value;
}
}
/// <summary>
/// Called when it's time to execute this storyboard action
/// </summary>
internal override void Invoke( FrameworkElement containingFE, FrameworkContentElement containingFCE, Storyboard storyboard )
{
Debug.Assert( containingFE != null || containingFCE != null,
"Caller of internal function failed to verify that we have a FE or FCE - we have neither." );
if( containingFE != null )
{
storyboard.SetSpeedRatio(containingFE, SpeedRatio);
}View on GitHub (pinned to 81131a70a4)