dotnet/reactive · error · InvalidOperationException
MORE_THAN_ONE_ELEMENT
Error message
MORE_THAN_ONE_ELEMENT
What it means
SingleOrDefaultAsync throws InvalidOperationException(MORE_THAN_ONE_ELEMENT) from OnNext when a second element arrives after one was already observed. 'OrDefault' only relaxes the empty case, not the uniqueness requirement: the sequence must contain at most one element. The error is forwarded via OnError as soon as the duplicate value is seen, cancelling further processing.
Solutions
- Use Take(1) (or FirstOrDefaultAsync) if only the first element is needed and duplicates are expected.
- Use .DistinctUntilChanged() before SingleOrDefaultAsync if duplicate values should be collapsed.
- If the source is truly required to be unique, fix the producer so it never emits more than one value.
- Catch InvalidOperationException from the subscription and fall back to the default or last-known value.
Example fix
// before var value = await updates.SingleOrDefaultAsync(); // throws if 2+ updates arrive // after var value = await updates.Take(1).LastOrDefaultAsync(); // first update, no throw // or if uniqueness is contractual: var value = await updates.DistinctUntilChanged().SingleOrDefaultAsync();
Defensive patterns
Strategy: validation
Validate before calling
// Enforce single-emission at the producer, or buffer and check count:
var buffered = await source.ToList();
if (buffered.Count > 1) { /* handle duplicates before calling SingleOrDefaultAsync */ } Type guard
static bool HasAtMostOne<T>(IList<T> buffered) => buffered.Count <= 1;
Try / catch
source.SingleOrDefaultAsync().Subscribe(
value => { /* use value or default */ },
ex => { if (ex is InvalidOperationException) { /* duplicate element path: fall back to First */ } else throw ex; }); Prevention
- Remember OrDefault only covers the empty case, not duplicates.
- Use Take(1)/FirstOrDefaultAsync when cardinality is not guaranteed.
- Add .DistinctUntilChanged() when duplicate notifications are expected from hot sources.
- Model 'at most one' semantics at the producer with a Replay(1)/BehaviorSubject rather than hoping downstream.
When it happens
Trigger: Calling Observable.SingleOrDefaultAsync(source) on a hot or cold observable that emits two or more OnNext values. Raised in the observer's OnNext override at SingleOrDefaultAsync.cs:38 when _seenValue is already true.
Common situations: Assuming SingleOrDefault behaves like ElementAt(0)/First with a fallback; streams that legitimately emit multiple updates (e.g. repeated config pushes, retries emitting duplicates); replacing First with SingleOrDefault without checking cardinality guarantees.
Related errors
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/6d603b5c173a6086.
Report an issue: GitHub.
Appendix: source
Thrown at Rx.NET/Source/src/System.Reactive/Linq/Observable/SingleOrDefaultAsync.cs:38
protected override void Run(_ sink) => sink.Run(_source);
internal sealed class _ : Sink<TSource, TSource?>
{
private TSource? _value;
private bool _seenValue;
public _(IObserver<TSource?> observer)
: base(observer)
{
}
public override void OnNext(TSource value)
{
if (_seenValue)
{
try
{
throw new InvalidOperationException(Strings_Linq.MORE_THAN_ONE_ELEMENT);
}
catch (Exception e)
{
ForwardOnError(e);
}
return;
}
_value = value;
_seenValue = true;
}
public override void OnCompleted()
{
ForwardOnNext(_value);
ForwardOnCompleted();
}
}View on GitHub (pinned to 94b5d5ab91)