stride3d/stride · error · InvalidOperationException

Something should handle controller disconnect

Error message

Something should handle controller disconnect

What it means

GameControllerSDL.Dispose closes the SDL joystick and then invokes the Disconnected event; if no handler is attached (Disconnected == null), it throws, because the source would never learn the controller went away and would keep a dangling device. The library mandates that someone must handle controller disconnect before a controller is disposed.

Solutions

  1. Ensure InputSourceSDL (or your owning code) subscribes to controller.Disconnected before any Dispose can occur
  2. Do not dispose a controller without a Disconnected handler in place
  3. In custom code, attach a no-op or logging handler if you don't need disconnect logic: controller.Disconnected += (s,e) => {};
  4. If this fires during shutdown, unregister the device from the source before disposing

Example fix

// before
controller.Dispose(); // Disconnected == null -> throws
// after
controller.Disconnected += (sender, args) => { /* handle removal */ };
controller.Dispose();
Defensive patterns

Strategy: validation

Validate before calling

controller.Disconnected += OnControllerDisconnected;
controller.Dispose();

Try / catch

try { controller.Dispose(); } catch (InvalidOperationException ex) { log.Warn($"Controller {controller.Id} disposed without disconnect handler"); }

Prevention

When it happens

Trigger: Disposing a GameControllerSDL while its Disconnected event has no subscribers — typically when the source removed its event handler, or the controller object is disposed before being wired to the source's handling code.

Common situations: SDL joystick hot-unplug triggering source-side cleanup that disposes the controller before re-subscribing; user code creating GameControllerSDL instances directly in tests without attaching Disconnected; refactors that accidentally unsubscribe the handler first.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/ff5ba193b2dfed15. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Input/SDL/GameControllerSDL.cs:78

        public override Guid ProductId { get; }

        public override IInputSource Source { get; }

        public override IReadOnlyList<GameControllerButtonInfo> ButtonInfos => buttonInfos;

        public override IReadOnlyList<GameControllerAxisInfo> AxisInfos => axisInfos;

        public override IReadOnlyList<GameControllerDirectionInfo> DirectionInfos => povControllerInfos;

        public event EventHandler Disconnected;

        public void Dispose()
        {
            if (!disposed)
            {
                SDL.JoystickClose(joystick);
                if (Disconnected == null)
                    throw new InvalidOperationException("Something should handle controller disconnect");
                Disconnected.Invoke(this, null);
                disposed = true;
            }
        }

        public override void Update(List<InputEvent> inputEvents)
        {
            if (SDL.JoystickGetAttached(joystick) == SdlBool.False)
            {
                Dispose();
                return;
            }

            for (int i = 0; i < buttonInfos.Count; i++)
            {
                HandleButton(i, SDL.JoystickGetButton(joystick, i) != 0);
            }

View on GitHub (pinned to 96fad776d2)