stride3d/stride · error · InvalidOperationException

DirectInput GameController already opened

Error message

DirectInput GameController already opened {deviceInstance.InstanceGuid}/{deviceInstance.InstanceName}

What it means

InputSourceWindowsDirectInput.OpenDevice throws InvalidOperationException when a DirectInput device with the same InstanceGuid is already present in the Devices dictionary. Scan enumerates devices repeatedly, and opening the same device twice would create duplicate controllers with conflicting state.

Solutions

  1. Check Devices.ContainsKey(deviceInstance.InstanceGuid) before calling OpenDevice.
  2. Ensure disconnected devices are removed from Devices before rescanning.
  3. Guard against concurrent/redundant Scan calls in custom input code.

Example fix

// before
inputSource.OpenDevice(deviceInstance);
// after
if (!inputSource.Devices.ContainsKey(deviceInstance.InstanceGuid))
    inputSource.OpenDevice(deviceInstance);
Defensive patterns

Strategy: validation

Validate before calling

if (!inputSource.Devices.ContainsKey(deviceInstance.InstanceGuid))
    inputSource.OpenDevice(deviceInstance);

Type guard

bool IsOpen(GameControllerDirectInput c, Guid instanceGuid) => c != null && inputSource.Devices.ContainsKey(instanceGuid);

Try / catch

try
{
    inputSource.OpenDevice(deviceInstance);
}
catch (InvalidOperationException ex) when (ex.Message.StartsWith("DirectInput GameController already opened"))
{
    // Device already tracked; ignore or refresh its state.
}

Prevention

When it happens

Trigger: Scan() runs while Devices already contains an entry for deviceInstance.InstanceGuid — e.g. two Scan passes without removal, or a device re-enumerated after an unplug that was not deregistered.

Common situations: Plugging a controller in and out quickly so the removal queue lags behind rescan; window focus changes triggering rescan while the device is still registered; custom code calling Scan concurrently.

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

Appendix: source

Thrown at sources/engine/Stride.Input/Windows/InputSourceWindowsDirectInput.cs:163

                if (!Devices.ContainsKey(device.InstanceGuid))
                {
                    OpenDevice(device);
                }
            }
        }

        /// <summary>
        /// Opens a new game controller or gamepad
        /// </summary>
        /// <param name="deviceInstance">The device instance</param>
        public void OpenDevice(DeviceInstance deviceInstance)
        {
            // Ignore XInput devices since they are handled by XInput
            if (IsXInputDevice(deviceInstance.ProductGuid))
                return;

            if (Devices.ContainsKey(deviceInstance.InstanceGuid))
                throw new InvalidOperationException($"DirectInput GameController already opened {deviceInstance.InstanceGuid}/{deviceInstance.InstanceName}");

            GameControllerDirectInput controller;
            try
            {
                controller = new GameControllerDirectInput(this, directInput, deviceInstance);
            }
            catch (SharpDXException)
            {
                // Some failure occured during device creation
                return;
            }

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

View on GitHub (pinned to 96fad776d2)