stride3d/stride · error · InvalidOperationException

Overlapping update commands

Error message

Overlapping update commands

What it means

PlayerController.TryMove starts an asynchronous pathfinding move and stores state in taskCompletionSource. If a previous move is still in flight (taskCompletionSource not yet cleared), calling TryMove again would overwrite that state, so the library throws this InvalidOperationException to prevent overlapping navigation commands.

Solutions

  1. Await or observe the Task<MoveResult> from the previous TryMove before issuing a new one.
  2. Track the in-flight task in a field and skip/requeue new moves while it is not completed.
  3. Call Reset() (which clears the pending state) before starting a new move if abandoning the current one.
  4. Ensure the pending move always completes (check MoveResult failures) so taskCompletionSource gets cleared.

Example fix

// before
// in Update()
playerController.TryMove(destination); // every frame while one is pending
// after
if (currentMove == null || currentMove.IsCompleted)
    currentMove = await playerController.TryMove(destination);
Defensive patterns

Strategy: type-guard

Validate before calling

if (playerController.IsMoving) return; // don't call TryMove while a move is pending

Type guard

bool CanMove(PlayerController pc) => pc.IsMoving == false;

Try / catch

try { await playerController.TryMove(dest); } catch (InvalidOperationException ex) when (ex.Message == "Overlapping update commands") { /* skip or cancel the previous move, then retry */ }

Prevention

When it happens

Trigger: Calling TryMove while a previous TryMove task is still pending (not completed and its completion source not reset), e.g. issuing a new move command every frame without awaiting the returned Task.

Common situations: Calling TryMove in Update() without awaiting/monitoring the previous move result; user input retriggering movement before the character reached the destination; a move task that never completes keeping taskCompletionSource non-null forever.

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/d7b3d1d8662915c8. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Navigation.Tests/PlayerController.cs:162

                Character.SetVelocity(moveDirection * Speed);
            }
            else
            {
                // No target
                HaltMovement();
            }
        }

        public void UpdateSpawnPosition()
        {
            Entity.Transform.UpdateWorldMatrix();
            SpawnPosition = Entity.Transform.WorldMatrix.TranslationVector;
        }

        public Task<MoveResult> TryMove(Vector3 destination)
        {
            if (taskCompletionSource != null)
                throw new InvalidOperationException("Overlapping update commands");

            pendingResult = new MoveResult();
            pendingResult.Start = Entity.Transform.WorldMatrix.TranslationVector;
            pendingResult.StartTime = Game.UpdateTime.Total;
            pendingResult.Success = false;

            // Generate a new path using the navigation component
            pathToDestination.Clear();
            if (Navigation.TryFindPath(destination, pathToDestination))
            {
                // Skip the points that are too close to the player
                waypointIndex = 0;
                while (!ReachedDestination && (CurrentWaypoint - Entity.Transform.WorldMatrix.TranslationVector).Length() < 0.25f)
                {
                    waypointIndex++;
                }

                // Add destination point

View on GitHub (pinned to 96fad776d2)