stride3d/stride · error · InvalidOperationException

XInput device already opened

Error message

XInput device already opened {index}

What it means

InputSourceWindowsXInput.OpenDevice throws InvalidOperationException when devices[index] is already occupied, i.e. the gamepad at that XInput slot was already opened. Only one GamePadXInput may own a controller slot; opening a second would produce duplicate event streams.

Solutions

  1. Check devices[index] != null before calling OpenDevice.
  2. Wait for/trigger the pending devicesToRemove cleanup before reopening the slot.
  3. Close or dispose the existing GamePadXInput before opening a new one on the same index.

Example fix

// before
inputSource.OpenDevice(0);
inputSource.OpenDevice(0); // crash
// after
if (inputSource.Devices[0] == null)
    inputSource.OpenDevice(0);
Defensive patterns

Strategy: validation

Validate before calling

if (inputSource.Devices[index] == null)
    inputSource.OpenDevice(index);

Type guard

bool SlotIsFree(int i) => inputSource.Devices[i] == null;

Try / catch

try
{
    inputSource.OpenDevice(index);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("already opened"))
{
    // Slot already in use; reuse the existing gamepad instead.
}

Prevention

When it happens

Trigger: Calling OpenDevice(index) twice for the same slot without closing the first, or Scan running again while the device is still registered (devicesToRemove not yet processed).

Common situations: Rapid unplug/replug before the pending removal is flushed; manual double-open in custom gamepad management; concurrent Scan invocations.

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/0dc802331cc66427. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Input/Windows/InputSourceWindowsXInput.cs:110

                    if (controllers[i].IsConnected)
                    {
                        OpenDevice(i);
                    }
                }
            }
        }

        /// <summary>
        /// Opens a new gamepad
        /// </summary>
        /// <param name="instance">The gamepad</param>
        public void OpenDevice(int index)
        {
            if (index < 0 || index >= XInputGamePadCount)
                throw new IndexOutOfRangeException($"Invalid XInput device index {index}");

            if (devices[index] != null)
                throw new InvalidOperationException($"XInput device already opened {index}");

            var newGamepad = new GamePadXInput(this, controllers[index], controllerIds[index], index);
            newGamepad.Disconnected += (sender, args) =>
            {
                // Queue device for removal
                devicesToRemove.Add(index);
            };

            devices[index] = newGamepad;
            RegisterDevice(newGamepad);
        }
    }
}

#endif

View on GitHub (pinned to 96fad776d2)