dotnet/reactive · error · InvalidOperationException
MORE_THAN_ONE_MATCHING_ELEMENT
Error message
MORE_THAN_ONE_MATCHING_ELEMENT
What it means
The predicate-based overload of SingleOrDefaultAsync throws InvalidOperationException(MORE_THAN_ONE_MATCHING_ELEMENT) in OnNext when a second element passes the predicate after one already matched. The operator asserts that at most one element of the source satisfies the filter; any additional match is a contract violation surfaced via OnError.
Solutions
- Tighten the predicate so it matches exactly one element (filter on a true unique key).
- Use .Where(predicate).Take(1) plus FirstOrDefaultAsync when multiple matches are legal and any one suffices.
- Deduplicate the source first (Distinct/DistinctBy semantics via GroupBy+SelectMany) before applying the operator.
- Catch the InvalidOperationException and decide how to handle genuinely duplicated matches.
Example fix
// before var user = await users.SingleOrDefaultAsync(u => u.Email == email); // throws on duplicate emails // after var user = await users.Where(u => u.Email == email).Take(1).LastOrDefaultAsync(); // or enforce uniqueness upstream on the email column
Defensive patterns
Strategy: validation
Validate before calling
// Verify predicate uniqueness against the data before relying on the operator:
int matches = await source.Where(predicate).Count();
if (matches > 1) { /* resolve duplicates (tighten key, deduplicate) first */ } Type guard
static bool MatchesAtMostOne<T>(IList<T> items, Func<T, bool> predicate) => items.Count(predicate) <= 1;
Try / catch
source.SingleOrDefaultAsync(predicate).Subscribe(
value => { /* use value or default */ },
ex => { if (ex is InvalidOperationException) { /* multiple matches: log and choose first */ } else throw ex; }); Prevention
- Filter on genuinely unique keys (IDs), not on fuzzy fields like names or emails unless uniqueness is enforced upstream.
- Prefer .Where(predicate).Take(1).LastOrDefaultAsync() when any single match suffices.
- Deduplicate source data (DB unique constraints, Distinct) before it reaches the stream.
- Add tests with duplicate-match data for every predicate-based SingleOrDefault call site.
When it happens
Trigger: Calling Observable.SingleOrDefaultAsync(source, predicate) where two or more source elements satisfy the predicate. Raised in the OnNext path at SingleOrDefaultAsync.cs:106 after evaluating the predicate to true while _seenValue is already true.
Common situations: Filtering by a key assumed unique (e.g. x => x.Id == targetId) against data containing duplicates after a merge/import; predicates that are looser than intended (matching multiple rows in a DB-backed stream); schema changes that break a uniqueness assumption.
Related errors
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/a0d0c52351a66ead.
Report an issue: GitHub.
Appendix: source
Thrown at Rx.NET/Source/src/System.Reactive/Linq/Observable/SingleOrDefaultAsync.cs:106
var b = false;
try
{
b = _predicate(value);
}
catch (Exception ex)
{
ForwardOnError(ex);
return;
}
if (b)
{
if (_seenValue)
{
try
{
throw new InvalidOperationException(Strings_Linq.MORE_THAN_ONE_MATCHING_ELEMENT);
}
catch (Exception e)
{
ForwardOnError(e);
}
return;
}
_value = value;
_seenValue = true;
}
}
public override void OnCompleted()
{
ForwardOnNext(_value);
ForwardOnCompleted();
}View on GitHub (pinned to 94b5d5ab91)