stride3d/stride · error · InvalidOperationException
Control is already disposed
Error message
Control is already disposed
What it means
SDLMessageLoop.Control's setter validates that the control being attached is not disposed, throwing 'Control is already disposed' otherwise. The message loop wraps a window/control to pump SDL events; attaching a dead control would leave the loop pumping events on a destroyed window.
Solutions
- Create a fresh window/control before assigning it to SDLMessageLoop.Control instead of reusing a disposed one.
- Ensure disposal order: dispose the message loop before disposing the control, and don't reassign a disposed control afterwards.
- Guard reattachment code: only set Control when control.IsDisposed is false; otherwise recreate.
Example fix
// before
messageLoop.Control = oldWindow; // oldWindow.IsDisposed == true
// after
if (oldWindow.IsDisposed)
oldWindow = new Window("Stride");
messageLoop.Control = oldWindow; Defensive patterns
Strategy: validation
Validate before calling
if (control.IsDisposed)
control = new Window("Stride"); // recreate before attaching
messageLoop.Control = control; Type guard
bool Attachable(Control c) => !c.IsDisposed;
Try / catch
try { messageLoop.Control = control; }
catch (InvalidOperationException ex) when (ex.Message.Contains("already disposed"))
{
control = new Window("Stride");
messageLoop.Control = control;
} Prevention
- Never reuse window references after disposal; recreate instead.
- Dispose the message loop before disposing its control.
- Track window lifetime explicitly in custom host code.
When it happens
Trigger: Assigning the Control property (or constructing SDLMessageLoop) with a control whose IsDisposed is true — e.g. using a window that was already closed/disposed, or re-using a GameWindow after teardown.
Common situations: Restarting a game loop with a stale window reference after the previous run was disposed, double-disposal in custom host code, or reattaching controls across game restarts.
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
- Cannot allocate SDL Window:
- Cannot get Window information:
- A dependency object is already attached to this instance of…
- A dependency object must be attached in order to register a…
- A dependency object must be attached in order to unregister…
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/f7bfaf314ebf9436.
Report an issue: GitHub.
Appendix: source
Thrown at sources/engine/Stride.Games/SDL/SDLMessageLoop.cs:69
{
get
{
return control;
}
set
{
if (control == value) return;
// Remove any previous control
if (control != null && !switchControl)
{
isControlAlive = false;
control.Disposed -= ControlDisposed;
}
if (value != null && value.IsDisposed)
{
throw new InvalidOperationException("Control is already disposed");
}
control = value;
switchControl = true;
}
}
/// <summary>
/// Calls this method on each frame.
/// </summary>
/// <returns><c>true</c> if if the control is still active, <c>false</c> otherwise.</returns>
/// <exception cref="System.InvalidOperationException">An error occurred </exception>
public bool NextFrame()
{
// Setup new control
// TODO this is not completely thread-safe. We should use a lock to handle this correctly
if (switchControl && control != null)
{View on GitHub (pinned to 96fad776d2)