stride3d/stride · error · ObjectDisposedException

This view model has already been disposed.

Error message

This view model has already been disposed.

What it means

Thrown by ViewModelBase.EnsureNotDestroyed when a member of an already-destroyed view model is accessed. View models implement Destroy for cleanup and set IsDestroyed; methods call EnsureNotDestroyed (passing optionally their name for the ObjectDisposedException) to refuse further use after destruction. This fires when editor code keeps a reference to a view model after its owning window/asset editor has closed and calls into it.

Solutions

  1. Check IsDestroyed before using the view model, or wrap usage in a lifetime check
  2. Unsubscribe handlers and cancel async work when the view model is disposed
  3. Fix the ownership/lifetime mismatch so the view model is not used after disposal

Example fix

// before
viewModel.SomeProperty = x; // may throw if disposed
// after
if (!viewModel.IsDestroyed) viewModel.SomeProperty = x;
Defensive patterns

Strategy: validation

Validate before calling

if (viewModelBase.IsDestroyed)
    return; // skip the update; view model already disposed

Type guard

bool IsAlive(ViewModelBase vm) => !vm.IsDestroyed;

Try / catch

try { vm.DoWork(); }
catch (ObjectDisposedException) { /* view model already disposed; ignore or unsubscribe */ }

Prevention

When it happens

Trigger: Calling any protected API that calls EnsureNotDestroyed after the view model's Dispose has been invoked — e.g. touching properties, services, or commands of a disposed view model.

Common situations: Event handlers or async continuations firing after a view model was disposed (window/editor closed); long-lived references to view models kept after their owner was destroyed.

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

Appendix: source

Thrown at sources/presentation/Stride.Core.Presentation/ViewModels/ViewModelBase.cs:60

    /// <inheritdoc/>
    /// <remarks>
    /// Derived class should override this method, implement specific cleanup and then call the base implementation.
    /// </remarks>
    public virtual void Destroy()
    {
        IsDestroyed = true;
    }

    /// <summary>
    /// Checks whether this view model 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 ?? GetType().Name, "This view model has already been disposed.");
        }
    }

    /// <summary>
    /// Sets the value of a field to the given value. Both values are compared with the default <see cref="EqualityComparer{T}"/>, and if they are equals,
    /// this method does nothing. If they are different, the <see cref="PropertyChanging"/> event will be raised first, then the field value will be modified,
    /// and finally the <see cref="PropertyChanged"/> event will be raised.
    /// </summary>
    /// <typeparam name="T">The type of the field.</typeparam>
    /// <param name="field">A reference to the field to set.</param>
    /// <param name="value">The new value to set.</param>
    /// <param name="propertyName">The name of the property that must be notified as changing/changed. Can use <see cref="CallerMemberNameAttribute"/>.</param>
    /// <returns><c>True</c> if the field was modified and events were raised, <c>False</c> if the new value was equal to the old one and nothing was done.</returns>
    protected bool SetValue<T>([NotNullIfNotNull(nameof(value))] ref T field, T value, [CallerMemberName] string propertyName = null!)
    {
        return SetValue(ref field, value, null, [propertyName]);
    }

View on GitHub (pinned to 96fad776d2)