stride3d/stride · error · InvalidOperationException

Input device was not registered

Error message

Input device was not registered

What it means

OnInputDeviceRemoved validates that the device being removed is currently registered; if devices.Contains(device) is false, Stride throws InvalidOperationException, preventing double-removal or removal of foreign devices from internal collections.

Solutions

  1. Only raise CollectionChanged Remove for devices previously announced with Add.
  2. Guard custom source teardown so each device removal fires once.
  3. Verify device lifetime management in the custom IInputSource implementation.

Example fix

// before
Devices.Remove(device); Devices.Remove(device); // second raise throws
// after
if (Devices.Contains(device)) Devices.Remove(device);
Defensive patterns

Strategy: type-guard

Validate before calling

bool CanRemove(IInputDevice d) => d != null && devices.Contains(d);

Type guard

bool IsRegistered(InputManager input, IInputDevice d) => input.Sources.SelectMany(s => s.Devices).Any(x => ReferenceEquals(x, d));

Try / catch

try { /* source teardown */ }
catch (InvalidOperationException ex) when (ex.Message == "Input device was not registered") { /* already removed: ignore */ }

Prevention

When it happens

Trigger: A source raising a Remove notification for a device that was never added, or raising Remove twice for the same device; a device from one source being removed via another source's collection.

Common situations: Custom input sources with buggy device lifecycle (double Remove on shutdown); device removal racing with its own earlier removal; sources disposing without having added devices.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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

Appendix: source

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

            }
            else if (device is IGamePadDevice)
            {
                RegisterGamePad((IGamePadDevice)device);
                gamePads.Sort((l, r) => -l.Priority.CompareTo(r.Priority));
            }
            else if (device is ISensorDevice)
            {
                RegisterSensor((ISensorDevice)device);
            }
            UpdateConnectedDevices();

            DeviceAdded?.Invoke(this, new DeviceChangedEventArgs { Device = device, Source = source, Type = DeviceChangedEventType.Added });
        }

        private void OnInputDeviceRemoved(IInputDevice device)
        {
            if (!devices.Contains(device))
                throw new InvalidOperationException("Input device was not registered");

            var source = device.Source;
            devices.Remove(device);
            devicesById.Remove(device.Id);

            if (device is IKeyboardDevice)
            {
                UnregisterKeyboard((IKeyboardDevice)device);
            }
            else if (device is IPointerDevice)
            {
                UnregisterPointer((IPointerDevice)device);
            }
            else if (device is IGameControllerDevice)
            {
                UnregisterGameController((IGameControllerDevice)device);
            }
            else if (device is IGamePadDevice)

View on GitHub (pinned to 96fad776d2)