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
- Check joystickInstanceIdToDeviceId before calling OpenDevice and skip if the instance id is already present
- Ensure device-removed handling runs so stale instance ids are cleaned before re-open
- Debounce SDL controller added/removed events before opening
- 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
- Process SDL joystick added events idempotently
- Ensure removal handling clears instance-id mappings before the same id can be reopened
- Debounce rapid connect/disconnect events from the OS
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
- Something should handle controller disconnect
- This GamePad's index can not be changed
- Not a valid gamepad
- Gamepad index was out of range
- Can not set more than one button at a time
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)