stride3d/stride · error · InvalidOperationException
Trying to await end of an animation which is not playing
Error message
Trying to await end of an animation which is not playing
What it means
AnimationComponent.Ended returns a Task that completes when the given PlayingAnimation finishes. It throws InvalidOperationException if the animation instance is not in the current PlayingAnimations collection, because there is nothing to await. This typically means the animation already finished and was removed, or was never started on this component.
Solutions
- Check PlayingAnimations.Contains(animation) before calling Ended, or wrap in try/catch for InvalidOperationException.
- Call Ended immediately after starting the animation (Crossfade/Blend return the PlayingAnimation).
- Track the returned PlayingAnimation and only await once, before it completes.
Example fix
// before
await animComponent.Ended(oldAnim); // may already have been removed
// after
if (animComponent.PlayingAnimations.Contains(oldAnim))
await animComponent.Ended(oldAnim); Defensive patterns
Strategy: try-catch
Validate before calling
if (animComponent.PlayingAnimations.Contains(pa))
await animComponent.Ended(pa); Try / catch
try { await animComponent.Ended(pa); }
catch (InvalidOperationException) { /* animation already finished/removed; nothing to await */ } Prevention
- Capture the PlayingAnimation returned by Crossfade/Blend and await Ended immediately.
- Never await the same PlayingAnimation's Ended twice.
- Account for crossfades removing animations when their weight reaches zero.
When it happens
Trigger: Calling Ended(pa) after the animation completed and was removed from PlayingAnimations, awaiting on a PlayingAnimation obtained from a different AnimationComponent, or a stale reference captured before the list was cleared.
Common situations: Awaiting an animation twice, awaiting after a crossfade faded the animation's weight to zero and it was culled, or race conditions where the animation ends between fetching the reference and calling Ended.
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
- First operation should be a push
- Stack should end up with result.
- This AnimationClip is frozen
- A disposed operation cannot be rollbacked.
- A disposed operation cannot be rollforwarded.
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/fdbdb3b4582257eb.
Report an issue: GitHub.
Appendix: source
Thrown at sources/engine/Stride.Engine/Engine/AnimationComponent.cs:201
return new PlayingAnimation(name, Animations[name]);
}
/// <summary>
/// Returns an awaitable object that will be completed when the animation is removed from the PlayingAnimation list.
/// </summary>
/// <remarks>
/// This happens when:
/// <list type="bullet">
/// <item>RepeatMode is PlayOnce and animation reached end</item>
/// <item>Animation faded out completely (due to blend to 0.0 or crossfade out)</item>
/// <item>Animation was manually removed from AnimationComponent.PlayingAnimations</item>
/// </list>
/// </remarks>
/// <returns></returns>
public Task Ended(PlayingAnimation animation)
{
if (!PlayingAnimations.Contains(animation))
throw new InvalidOperationException("Trying to await end of an animation which is not playing");
if (animation.EndedTCS == null)
{
Interlocked.CompareExchange(ref animation.EndedTCS, new TaskCompletionSource<bool>(), null);
}
return animation.EndedTCS.Task;
}
}
public interface IBlendTreeBuilder
{
void BuildBlendTree(List<AnimationOperation> animationList);
}
}
View on GitHub (pinned to 96fad776d2)