stride3d/stride · error · ObjectDisposedException

This controller has already been disposed.

Error message

This controller has already been disposed.

What it means

EnsureNotDestroyed throws ObjectDisposedException when EditorGameController.IsDestroyed is true. After Destroy() completes, all game-side resources (game thread, form, services) are gone, so any further use — StartGame, GetService, GetMousePositionInScene, etc. — is invalid and guarded by this method called from all public entry points.

Solutions

  1. Check controller.IsDestroyed before every use and reacquire the controller from the editor when the asset is reopened.
  2. Catch ObjectDisposedException around controller calls in long-lived background code.
  3. Subscribe to the asset editor's close event to stop using the controller at the right time.

Example fix

// before
controller.GetService<IMyService>().DoWork();
// after
if (!controller.IsDestroyed)
    controller.GetService<IMyService>().DoWork();
Defensive patterns

Strategy: try-catch

Validate before calling

if (controller.IsDestroyed)
    return; // controller already disposed

Type guard

bool IsAlive(EditorGameControllerBase c) => !c.IsDestroyed;

Try / catch

try { controller.GetService<T>(); }
catch (ObjectDisposedException) { /* reacquire controller from the reopened asset editor */ }

Prevention

When it happens

Trigger: Calling any public controller method (StartGame, GetService<T>, Destroy again, GetMousePositionInScene, TriggerActiveRenderStageReevaluation, FindGameSidePart) after the editor asset was closed and the controller disposed.

Common situations: Holding a stale controller reference after closing/reopening an asset editor; background jobs or timers that outlive the asset session; double teardown logic calling Destroy twice.

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

Appendix: source

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

        {
            throw new NotSupportedException();
        }

        /// <inheritdoc/>
        TResult IDispatcherService.Invoke<TResult>(Func<TResult> callback)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Checks whether this controller has been disposed, and throws an <see cref="ObjectDisposedException"/> if it is the case.
        /// </summary>
        /// <param name="name">The name to supply to the <see cref="ObjectDisposedException"/>.</param>
        protected void EnsureNotDestroyed(string name = null)
        {
            if (IsDestroyed)
            {
                throw new ObjectDisposedException(name ?? nameof(EditorGameController<TEditorGame>), "This controller has already been disposed.");
            }
        }

        protected virtual void InitializeServices([NotNull] EditorGameServiceRegistry services)
        {
            services.Add(new EditorGameDebugService());
            services.Add(RecoveryService = new EditorGameRecoveryService(Editor) { IsActive = true });
        }

        private void SceneGameRunThread()
        {
            // Create the form from this thread
            GameForm = new EmbeddedGameForm
            {
                TopLevel = false,
                Visible = false,
            };
            windowHandle = GameForm.Handle;

View on GitHub (pinned to 96fad776d2)