stride3d/stride · error · InvalidOperationException

SDL GameController already opened

Error message

SDL GameController already opened {deviceIndex}/{*(Guid*)&joystickId}/{joystickName}

What it means

InputSourceSDL.OpenDevice tracks already-open joysticks by joystick instance id in joystickInstanceIdToDeviceId. If OpenDevice is called for a device whose instance id is already mapped, it throws InvalidOperationException with the device index, GUID and name. Opening the same SDL game controller twice would create duplicate device objects for one physical device.

Solutions

  1. Check joystickInstanceIdToDeviceId before calling OpenDevice and skip if the instance id is already present
  2. Ensure device-removed handling runs so stale instance ids are cleaned before re-open
  3. Debounce SDL controller added/removed events before opening
  4. Log the device index/GUID from the message to identify the duplicated device and its source of double-open

Example fix

// before
OpenDevice(deviceIndex); // may already be open
// after
if (!joystickInstanceIdToDeviceId.ContainsKey(GetJoystickInstanceId(deviceIndex)))
    OpenDevice(deviceIndex);
Defensive patterns

Strategy: validation

Validate before calling

if (!joystickInstanceIdToDeviceId.ContainsKey(GetJoystickInstanceId(deviceIndex)))
    OpenDevice(deviceIndex);

Try / catch

try { sourceOpenDevice(deviceIndex); } catch (InvalidOperationException) { /* already open, ignore */ }

Prevention

When it happens

Trigger: SDL raising a device-added event twice for the same instance id (e.g. Connect/Disconnect events coalesced or replayed), or calling OpenDevice manually for an index that was already opened.

Common situations: SDL joystick hot-plug storms on some platforms/drivers; re-scanning device lists without clearing state; driver quirks where unplug/replug reuses the same instance id before cleanup ran.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at sources/engine/Stride.Input/SDL/InputSourceSDL.cs:102

        }

        public override void Scan()
        {
            for (int i = 0; i < SDL.NumJoysticks(); i++)
            { 
                if (!joystickInstanceIdToDeviceId.ContainsKey(GetJoystickInstanceId(i)))
                {
                    OpenDevice(i);
                }
            }
        }

        private void OpenDevice(int deviceIndex)
        {
            var joystickId = SDL.JoystickGetDeviceGUID(deviceIndex);
            var joystickName = SDL.JoystickNameForIndexS(deviceIndex);
            if (joystickInstanceIdToDeviceId.ContainsKey(GetJoystickInstanceId(deviceIndex)))
                throw new InvalidOperationException($"SDL GameController already opened {deviceIndex}/{*(Guid*)&joystickId}/{joystickName}");

            var controller = new GameControllerSDL(this, deviceIndex);

            IInputDevice resultingDevice = controller;

            // Find gamepad layout
            var layout = GamePadLayouts.FindLayout(this, controller);
            if (layout != null)
            {
                // Create a gamepad wrapping around the controller
                var gamePad = new GamePadSDL(this, inputManager, controller, layout);
                resultingDevice = gamePad; // Register gamepad instead
            }

            controller.Disconnected += (sender, args) =>
            {
                // Queue device for removal
                devicesToRemove.Add(resultingDevice.Id);

View on GitHub (pinned to 96fad776d2)