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
- Ensure the operation list starts with an AnimationOperation of type Push (the base/first animation).
- Fix the code that emits the operation sequence so the initial Push is never skipped.
- Validate/normalize the operation list before calling ComputeAnimationBlending.
- 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
- Build operation lists via a helper that always emits an initial Push
- Never slice an operation list starting at index > 0 without prepending a Push
- Unit-test operation-sequence generators for ordering invariants
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
- Stack should end up with result.
- blendOperation
- This AnimationClip is frozen
- Trying to await end of an animation which is not playing
- A disposed operation cannot be rollbacked.
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)