stride3d/stride · error · IndexOutOfRangeException

Gamepad index was out of range

Error message

Gamepad index was out of range

What it means

UnregisterGamePad frees the gamepad's slot in gamePadRequestedIndex. If gamePad.Index is negative or >= gamePadRequestedIndex.Count, the internal index table no longer matches the device and Stride throws IndexOutOfRangeException as an invariant failure rather than silently corrupting state.

Solutions

  1. Allocate indices only via InputManager.GetFreeGamePadIndex so Index stays in sync.
  2. Ensure each gamepad is unregistered exactly once (guard custom teardown code).
  3. Update Stride if the crash occurs during normal hot-plug with official backends.

Example fix

// before
gamePad.Index = 0; // hand-assigned, out of sync with table
// after
var idx = inputManager.GetFreeGamePadIndex(gamePad);
gamePad.RequestedIndex = idx;
Defensive patterns

Strategy: try-catch

Validate before calling

bool IndexValid(IGamePadDevice pad, int requestedIndexCount) => pad.Index >= 0 && pad.Index < requestedIndexCount;

Try / catch

try { /* unregister path runs internally */ }
catch (IndexOutOfRangeException ex) when (ex.Message == "Gamepad index was out of range")
{ logger.Warn(ex, "Stale gamepad index; forcing device list refresh"); }

Prevention

When it happens

Trigger: A gamepad with an out-of-sync Index (never allocated, or table shrunk) being unregistered; double-unregister of the same gamepad after its index was invalidated.

Common situations: Rapid connect/disconnect of controllers where a device is torn down twice; custom platform backends assigning Index without going through GetFreeGamePadIndex; shutdown ordering issues.

Related errors


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

Appendix: source

Thrown at sources/engine/Stride.Input/InputManager.cs:931

            gamePads.Add(gamePad);

            // Check if the gamepad provides an interface for assigning gamepad index
            if (gamePad.CanChangeIndex)
            {
                gamePad.Index = GetFreeGamePadIndex(gamePad);
            }
            
            // Handle later index changed
            gamePad.IndexChanged += GamePadOnIndexChanged;
            UpdateGamePadRequestedIndices();
        }

        private void UnregisterGamePad(IGamePadDevice gamePad)
        {
            // Free the gamepad index in the gamepad list
            // this will allow another gamepad to use this index again
            if (gamePadRequestedIndex.Count <= gamePad.Index || gamePad.Index < 0)
                throw new IndexOutOfRangeException("Gamepad index was out of range");

            gamePadRequestedIndex[gamePad.Index].Remove(gamePad);

            gamePads.Remove(gamePad);
            gamePad.IndexChanged -= GamePadOnIndexChanged;
        }

        private void RegisterGameController(IGameControllerDevice gameController)
        {
            gameControllers.Add(gameController);
        }

        private void UnregisterGameController(IGameControllerDevice gameController)
        {
            gameControllers.Remove(gameController);
        }

        private void GamePadOnIndexChanged(object sender, GamePadIndexChangedEventArgs e)

View on GitHub (pinned to 96fad776d2)