{"record":{"id":"39ae705dcbb4fbdd","repo":"stride3d/stride","slug":"input-device-with-id-device-id-already-registered","errorCode":null,"errorMessage":"Input device with Id {device.Id} already registered","messagePattern":"Input device with Id (.+?) already registered","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/engine/Stride.Input/InputSourceBase.cs","lineNumber":50,"sourceCode":"        }\n\n        /// <summary>\n        /// Unregisters all devices registered with <see cref=\"RegisterDevice\"/> which have not been unregistered yet\n        /// </summary>\n        public virtual void Dispose()\n        {\n            // Remove all devices, done by clearing the tracking dictionary\n            Devices.Clear();\n        }\n\n        /// <summary>\n        /// Adds the device to the list <see cref=\"Devices\"/>\n        /// </summary>\n        /// <param name=\"device\">The device</param>\n        protected void RegisterDevice(IInputDevice device)\n        {\n            if (Devices.ContainsKey(device.Id))\n                throw new InvalidOperationException($\"Input device with Id {device.Id} already registered\");\n\n            Devices.Add(device.Id, device);\n        }\n\n        /// <summary>\n        /// CRemoves the device from the list <see cref=\"Devices\"/>\n        /// </summary>\n        /// <param name=\"device\">The device</param>\n        protected void UnregisterDevice(IInputDevice device)\n        {\n            if (!Devices.Remove(device.Id))\n                throw new InvalidOperationException($\"Input device with Id {device.Id} was not registered\");\n        }\n    }\n}\n","sourceCodeStart":32,"sourceCodeEnd":66,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/engine/Stride.Input/InputSourceBase.cs#L32-L66","documentation":"InputSourceBase.RegisterDevice guards its Devices dictionary against duplicate keys. When a device implementation (or the owning InputSource) tries to register an IInputDevice whose Id is already present, the base class refuses rather than silently overwrite the existing device, throwing InvalidOperationException. This is an internal state invariant: each registered device must have a unique Id.","triggerScenarios":"Calling RegisterDevice twice with the same device instance, or with two distinct device objects that report the same Id (e.g. a device re-created after reconnect without unregistering the old one, or a subclass assigning a constant Id to multiple instances).","commonSituations":"Hot-plug handling that re-adds a controller on reconnect while the old entry is still registered; custom input sources that generate device IDs from index/name rather than a unique handle; game/editor reinitialization of the input system without disposing the previous source.","solutions":["Ensure UnregisterDevice is called before re-registering a device with the same Id","Give each device a unique Id (use instance handles/GUIDs, not names or indices)","Check Devices.ContainsKey(device.Id) before calling RegisterDevice and skip or remove the stale entry first","If re-creating a device on reconnect, dispose/unregister the old instance before constructing the new one"],"exampleFix":"// before\nsource.RegisterDevice(newGamePad); // old pad with same Id still registered\n// after\nvar old = source.Devices.Values.OfType<GamePadSimulated>().FirstOrDefault(d => d.Id == newGamePad.Id);\nif (old != null) source.RemoveGamePad(old);\nsource.RegisterDevice(newGamePad);","handlingStrategy":"validation","validationCode":"if (inputSource.Devices.ContainsKey(device.Id)) inputSource.UnregisterDevice(device); // or skip\ninputSource.RegisterDevice(device);","typeGuard":"bool IsRegistered(InputSourceBase source, IInputDevice d) => source.Devices.ContainsKey(d.Id);","tryCatchPattern":null,"preventionTips":["Generate device Ids from unique handles/GUIDs, never names or fixed indices","Always pair RegisterDevice with UnregisterDevice in add/remove event handlers","Make reconnect paths idempotent: unregister old entry before registering the replacement"],"tags":["input","csharp","duplicate-key","device-registration"],"backgroundTag":"internal-invariant-violation","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}