{"record":{"id":"f7bfaf314ebf9436","repo":"stride3d/stride","slug":"control-is-already-disposed-sdlmessageloop","errorCode":null,"errorMessage":"Control is already disposed","messagePattern":"Control is already disposed","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/engine/Stride.Games/SDL/SDLMessageLoop.cs","lineNumber":69,"sourceCode":"        {\n            get\n            {\n                return control;\n            }\n            set\n            {\n                if (control == value) return;\n\n                // Remove any previous control\n                if (control != null && !switchControl)\n                {\n                    isControlAlive = false;\n                    control.Disposed -= ControlDisposed;\n                }\n\n                if (value != null && value.IsDisposed)\n                {\n                    throw new InvalidOperationException(\"Control is already disposed\");\n                }\n\n                control = value;\n                switchControl = true;\n            }\n        }\n\n        /// <summary>\n        /// Calls this method on each frame.\n        /// </summary>\n        /// <returns><c>true</c> if if the control is still active, <c>false</c> otherwise.</returns>\n        /// <exception cref=\"System.InvalidOperationException\">An error occurred </exception>\n        public bool NextFrame()\n        {\n            // Setup new control\n            // TODO this is not completely thread-safe. We should use a lock to handle this correctly\n            if (switchControl && control != null)\n            {","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/engine/Stride.Games/SDL/SDLMessageLoop.cs#L51-L87","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nmessageLoop.Control = oldWindow; // oldWindow.IsDisposed == true\n// after\nif (oldWindow.IsDisposed)\n    oldWindow = new Window(\"Stride\");\nmessageLoop.Control = oldWindow;","handlingStrategy":"validation","validationCode":"if (control.IsDisposed)\n    control = new Window(\"Stride\"); // recreate before attaching\nmessageLoop.Control = control;","typeGuard":"bool Attachable(Control c) => !c.IsDisposed;","tryCatchPattern":"try { messageLoop.Control = control; }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"already disposed\"))\n{\n    control = new Window(\"Stride\");\n    messageLoop.Control = control;\n}","preventionTips":["Never reuse window references after disposal; recreate instead.","Dispose the message loop before disposing its control.","Track window lifetime explicitly in custom host code."],"tags":["sdl","window","lifecycle","disposed-object"],"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-23T08:17:48.524Z"}