{"record":{"id":"d7b3d1d8662915c8","repo":"stride3d/stride","slug":"overlapping-update-commands","errorCode":null,"errorMessage":"Overlapping update commands","messagePattern":"Overlapping update commands","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/engine/Stride.Navigation.Tests/PlayerController.cs","lineNumber":162,"sourceCode":"                Character.SetVelocity(moveDirection * Speed);\n            }\n            else\n            {\n                // No target\n                HaltMovement();\n            }\n        }\n\n        public void UpdateSpawnPosition()\n        {\n            Entity.Transform.UpdateWorldMatrix();\n            SpawnPosition = Entity.Transform.WorldMatrix.TranslationVector;\n        }\n\n        public Task<MoveResult> TryMove(Vector3 destination)\n        {\n            if (taskCompletionSource != null)\n                throw new InvalidOperationException(\"Overlapping update commands\");\n\n            pendingResult = new MoveResult();\n            pendingResult.Start = Entity.Transform.WorldMatrix.TranslationVector;\n            pendingResult.StartTime = Game.UpdateTime.Total;\n            pendingResult.Success = false;\n\n            // Generate a new path using the navigation component\n            pathToDestination.Clear();\n            if (Navigation.TryFindPath(destination, pathToDestination))\n            {\n                // Skip the points that are too close to the player\n                waypointIndex = 0;\n                while (!ReachedDestination && (CurrentWaypoint - Entity.Transform.WorldMatrix.TranslationVector).Length() < 0.25f)\n                {\n                    waypointIndex++;\n                }\n\n                // Add destination point","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/engine/Stride.Navigation.Tests/PlayerController.cs#L144-L180","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Await or observe the Task<MoveResult> from the previous TryMove before issuing a new one.","Track the in-flight task in a field and skip/requeue new moves while it is not completed.","Call Reset() (which clears the pending state) before starting a new move if abandoning the current one.","Ensure the pending move always completes (check MoveResult failures) so taskCompletionSource gets cleared."],"exampleFix":"// before\n// in Update()\nplayerController.TryMove(destination); // every frame while one is pending\n// after\nif (currentMove == null || currentMove.IsCompleted)\n    currentMove = await playerController.TryMove(destination);","handlingStrategy":"type-guard","validationCode":"if (playerController.IsMoving) return; // don't call TryMove while a move is pending","typeGuard":"bool CanMove(PlayerController pc) => pc.IsMoving == false;","tryCatchPattern":"try { await playerController.TryMove(dest); } catch (InvalidOperationException ex) when (ex.Message == \"Overlapping update commands\") { /* skip or cancel the previous move, then retry */ }","preventionTips":["Always await the Task returned by TryMove before issuing another move","Track the in-flight move Task in a field and gate new commands on IsCompleted","Use Reset() when abandoning an in-flight move","Avoid issuing TryMove from per-frame Update code without completion checks"],"tags":["stride","navigation","async","concurrency"],"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-15T23:17:13.987Z"}