stride3d/stride · error · InvalidOperationException

First operation should be a push

Error message

First operation should be a push

What it means

AnimationBlender.ComputeAnimationBlending treats the animation operation list as stack machine instructions; the very first operation must be a Push that seeds the result. If animationOperations[0].Type is not AnimationOperationType.Push, the blender's invariants cannot hold and it throws this InvalidOperationException.

Solutions

  1. Ensure the operation list starts with an AnimationOperation of type Push (the base/first animation).
  2. Fix the code that emits the operation sequence so the initial Push is never skipped.
  3. Validate/normalize the operation list before calling ComputeAnimationBlending.
  4. If the list can legitimately be empty, check animationOperations.Count > 0 and first type before invoking the blender.

Example fix

// before
blender.ComputeAnimationBlending(operations, ref result);

// after (pre-validation)
if (operations.Count == 0 || operations[0].Type != AnimationOperationType.Push)
    throw new ArgumentException("Operations must start with a Push");
blender.ComputeAnimationBlending(operations, ref result);
Defensive patterns

Strategy: validation

Validate before calling

// Guard before calling the blender
if (operations == null || operations.Count == 0 || operations[0].Type != AnimationOperationType.Push)
    throw new ArgumentException("Operation list must start with a Push");

Try / catch

try { blender.ComputeAnimationBlending(ops, ref result); }
catch (InvalidOperationException ex) when (ex.Message == "First operation should be a push") { /* rebuild op list starting with Push */ }

Prevention

When it happens

Trigger: Calling ComputeAnimationBlending with an operation list whose first entry is Pop, Blend, CheckTotal or any non-Push type — e.g. building the operation list manually or with an off-by-one that drops the initial Push at AnimationBlender.cs:306.

Common situations: Custom animation graph/tooling code constructing AnimationOperation lists by hand; truncated or incorrectly ordered operation sequences generated by an animation clip evaluator; rebasing/blending code emitting operations starting with a blend onto an empty stack.

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


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/c91c123da6a4c755. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Engine/Animations/AnimationBlender.cs:306

                }
            }
        }

        /// <summary>
        /// Computes the specified animation operations.
        /// </summary>
        /// <param name="animationOperations">The animation operations to perform.</param>
        /// <param name="result">The optional result (if not null, it expects the final stack to end up with this element).</param>
        public void Compute(List<AnimationOperation> animationOperations, ref AnimationClipResult result)
        {
            // Clear animation stack
            animationStack.Clear();

            // Apply first operation (should be a push), directly into result (considered first item in the stack)
            var animationOperation0 = animationOperations[0];

            if (animationOperation0.Type != AnimationOperationType.Push)
                throw new InvalidOperationException("First operation should be a push");

            // TODO: Either result is null (should have a Pop operation) or result is non null (stack end up being size 1)
            var hasResult = result != null;
            if (hasResult)
            {
                // Ensure there is enough size
                // TODO: Force result allocation to happen on user side?
                if (result.DataSize < structureSize)
                {
                    result.DataSize = structureSize;
                    result.Data = new byte[structureSize];
                }

                if (objectsSize > 0 && (result.Objects == null || result.Objects.Length < objectsSize))
                {
                    result.Objects = new UpdateObjectData[objectsSize];
                }

View on GitHub (pinned to 96fad776d2)