stride3d/stride · error · InvalidOperationException

This controller is beeing disposed.

Error message

This controller is beeing disposed.

What it means

EditorGameController.StartGame refuses to run when the controller is in the process of being destroyed (IsDestroying is true). Starting a game thread while teardown is in progress would create new game-side resources that no one will clean up, so it throws InvalidOperationException instead of EnsureNotDestroyed's ObjectDisposedException.

Solutions

  1. Check controller.IsDestroying / IsDestroyed before calling StartGame and skip the call.
  2. Await the editor's close/dispose completion before issuing a new StartGame for a replacement controller.
  3. Cancel pending StartGame work when the asset editor is closing (tie it to the asset lifecycle).

Example fix

// before
await controller.StartGame();
// after
if (!controller.IsDestroyed && !controller.IsDestroying)
    await controller.StartGame();
Defensive patterns

Strategy: validation

Validate before calling

if (controller.IsDestroyed || controller.IsDestroying)
    return false; // skip StartGame during teardown

Type guard

bool IsUsable(EditorGameControllerBase c) => !c.IsDestroyed && !c.IsDestroying;

Try / catch

try { await controller.StartGame(); }
catch (InvalidOperationException) { /* controller being disposed; abandon start */ }

Prevention

When it happens

Trigger: Calling StartGame() after Destroy() has begun (IsDestroying == true) but possibly before it fully completes — e.g. a fast asset close/reopen cycle, or an async task that was awaiting something and resumes StartGame after the editor closed the asset.

Common situations: Editor session or asset closing while a deferred start request is still pending; user closes the asset editor tab while game preview startup is in flight; race between dispose and initialization code.

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

Appendix: source

Thrown at sources/editor/Stride.Assets.Presentation/AssetEditors/GameEditor/Services/EditorGameController.cs:230

        [CanBeNull]
        protected abstract object FindPart(AbsoluteId partId);

        public T GetService<T>() where T : IEditorGameViewModelService
        {
            EnsureNotDestroyed();
            EnsureAssetAccess();
            if (IsDestroying || serviceRegistry == null)
                return default(T);
            return serviceRegistry.Get<T>();
        }

        /// <inheritdoc/>
        public async Task<bool> StartGame()
        {
            EnsureNotDestroyed();
            if (IsDestroying)
                throw new InvalidOperationException("This controller is beeing disposed.");
            // Run the game in a separate thread (create Form and run)
            sceneGameThread.Start();
            // Wait for the game to start
            await gameStartedTaskSource.Task;
            GameForm.MouseDown += (sender, e) => lastClickPosition = Control.MousePosition;
            // Initialize the WPF GameEngineHwndHost on this thread
            GameForm.Host = new GameEngineHost(windowHandle);

            // TODO: we could check if the game fails to create.
            return true;
        }

        public void OnHideGame()
        {
            Game.IsEditorHidden = true;
        }

        public void OnShowGame()

View on GitHub (pinned to 96fad776d2)