stride3d/stride · error · InvalidOperationException
Input Source already added
Error message
Input Source already added
What it means
When the Sources tracking collection changes, the InputManager validates each Add and throws InvalidOperationException if the same IInputSource instance is already present (duplicated via Count > 1). Each source may only be initialized and subscribed once.
Solutions
- Add the source only once; guard with Sources.Contains(source) before Add.
- Move source registration out of code that may re-execute (OnSceneLoad, Initialize).
- Create a new IInputSource instance instead of re-adding a used one.
Example fix
// before Sources.Add(mySource); // may run twice // after if (!Sources.Contains(mySource)) Sources.Add(mySource);
Defensive patterns
Strategy: validation
Validate before calling
if (!input.Sources.Contains(source)) input.Sources.Add(source);
Try / catch
try { input.Sources.Add(source); }
catch (InvalidOperationException ex) when (ex.Message == "Input Source already added") { /* already registered: ignore */ } Prevention
- Guard Add with Contains when initialization may re-run.
- Register sources in a single, idempotent startup path.
- Never share one source instance between multiple InputManagers.
When it happens
Trigger: Adding the same IInputSource instance to InputManager.Sources twice, e.g. calling Sources.Add(source) in code that runs again on scene reload or when Initialize is invoked twice.
Common situations: Game initialization re-run after reload; copy-pasted setup code adding a source in two places; sharing one source instance across multiple InputManagers.
Related errors
- The event type was not registered with the input manager…
- Input device was not registered
- Could not find asset
- Expect name1=value1;name2=value2 format.
- The upgrader overlaps with another upgrader.
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/cac309bf3e111fe0.
Report an issue: GitHub.
Appendix: source
Thrown at sources/engine/Stride.Input/InputManager.cs:597
/// <summary>
/// Resume all input sources.
/// </summary>
public void Resume()
{
foreach (var source in Sources)
{
source.Resume();
}
}
private void SourcesOnCollectionChanged(object o, TrackingCollectionChangedEventArgs e)
{
var source = (IInputSource)e.Item;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
if (Sources.Count(x => x == source) > 1)
throw new InvalidOperationException("Input Source already added");
EventHandler<TrackingCollectionChangedEventArgs> eventHandler = (sender, args) => InputDevicesOnCollectionChanged(source, args);
devicesCollectionChangedActions.Add(source, eventHandler);
source.Devices.CollectionChanged += eventHandler;
source.Initialize(this);
break;
case NotifyCollectionChangedAction.Remove:
source.Dispose();
source.Devices.CollectionChanged -= devicesCollectionChangedActions[source];
devicesCollectionChangedActions.Remove(source);
break;
default:
throw new ArgumentOutOfRangeException(nameof(e.Action));
}
}
/// <summary>
/// Registers an input event type to processView on GitHub (pinned to 96fad776d2)