{"record":{"id":"fdbdb3b4582257eb","repo":"stride3d/stride","slug":"trying-to-await-end-of-an-animation-which-is-not-playing","errorCode":null,"errorMessage":"Trying to await end of an animation which is not playing","messagePattern":"Trying to await end of an animation which is not playing","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/engine/Stride.Engine/Engine/AnimationComponent.cs","lineNumber":201,"sourceCode":"            return new PlayingAnimation(name, Animations[name]);\n        }\n\n        /// <summary>\n        /// Returns an awaitable object that will be completed when the animation is removed from the PlayingAnimation list.\n        /// </summary>\n        /// <remarks>\n        /// This happens when:\n        /// <list type=\"bullet\">\n        /// <item>RepeatMode is PlayOnce and animation reached end</item>\n        /// <item>Animation faded out completely (due to blend to 0.0 or crossfade out)</item>\n        /// <item>Animation was manually removed from AnimationComponent.PlayingAnimations</item>\n        /// </list>\n        /// </remarks>\n        /// <returns></returns>\n        public Task Ended(PlayingAnimation animation)\n        {\n            if (!PlayingAnimations.Contains(animation))\n                throw new InvalidOperationException(\"Trying to await end of an animation which is not playing\");\n\n            if (animation.EndedTCS == null)\n            {\n                Interlocked.CompareExchange(ref animation.EndedTCS, new TaskCompletionSource<bool>(), null);\n            }\n\n            return animation.EndedTCS.Task;\n        }\n    }\n\n    public interface IBlendTreeBuilder\n    {\n        void BuildBlendTree(List<AnimationOperation> animationList);\n    }\n}\n","sourceCodeStart":183,"sourceCodeEnd":217,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/engine/Stride.Engine/Engine/AnimationComponent.cs#L183-L217","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait animComponent.Ended(oldAnim); // may already have been removed\n// after\nif (animComponent.PlayingAnimations.Contains(oldAnim))\n    await animComponent.Ended(oldAnim);","handlingStrategy":"try-catch","validationCode":"if (animComponent.PlayingAnimations.Contains(pa))\n    await animComponent.Ended(pa);","typeGuard":null,"tryCatchPattern":"try { await animComponent.Ended(pa); }\ncatch (InvalidOperationException) { /* animation already finished/removed; nothing to await */ }","preventionTips":["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."],"tags":["animation","invalid-state","async"],"backgroundTag":"invalid-state-transition","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}