dotnet/reactive · error · InvalidOperationException

NO_MATCHING_ELEMENTS

Error message

NO_MATCHING_ELEMENTS

What it means

SingleAsync throws InvalidOperationException(NO_MATCHING_ELEMENTS) from its OnCompleted observer callback when the source completes without ever emitting a value. Single* operators require exactly one element; an empty sequence violates that contract, so the library signals the failure via OnError instead of returning a default. It is raised asynchronously during the terminal OnCompleted notification, not at subscription time.

Solutions

  1. Use SingleOrDefaultAsync instead of SingleAsync if an empty source is acceptable (it yields default(TSource)).
  2. Guard the sequence with .DefaultIfEmpty(...) before applying SingleAsync so OnCompleted always follows at least one value.
  3. Ensure upstream filtering cannot eliminate all elements, or handle the OnError(InvalidOperationException) explicitly in Subscribe.
  4. If the stream should never be empty, fix the producer that publishes zero elements.

Example fix

// before
var value = await source.SingleAsync(); // throws when source is empty

// after
var value = await source.SingleOrDefaultAsync(); // default(TSource) when empty
// or
var value = await source.DefaultIfEmpty(fallback).SingleAsync();
Defensive patterns

Strategy: validation

Validate before calling

// Only call SingleAsync when the sequence is guaranteed non-empty
// e.g. check emptiness first:
bool hasAny = await source.Any(); // .Any() returns IObservable<bool>
if (!hasAny) { /* use default / report empty */ }
else var value = await source.SingleAsync();

Type guard

static bool HasExactlyOne<T>(IList<T> buffered) => buffered.Count == 1;

Try / catch

source.SingleAsync().Subscribe(
    value => { /* use value */ },
    ex => { if (ex is InvalidOperationException) { /* empty sequence path */ } else throw ex; });

Prevention

When it happens

Trigger: Calling Observable.SingleAsync(source) (or SingleAsync with a predicate) on a source that emits zero OnNext notifications before OnCompleted. The throw happens inside SingleAsync's observer.OnCompleted override at SingleAsync.cs:140 when _seenValue is still false.

Common situations: Subscribing to an empty observable (e.g. an empty array converted with ToObservable, a filtered stream that matches nothing, or a backend query returning no rows) and then expecting a single value; race conditions where an upstream filter becomes stricter and the stream can legitimately go empty.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15). Data as JSON: /api/errors/337458dea7d8193e. Report an issue: GitHub.

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Linq/Observable/SingleAsync.cs:140

                            catch (Exception e)
                            {
                                ForwardOnError(e);
                            }
                            return;
                        }

                        _value = value;
                        _seenValue = true;
                    }
                }

                public override void OnCompleted()
                {
                    if (!_seenValue)
                    {
                        try
                        {
                            throw new InvalidOperationException(Strings_Linq.NO_MATCHING_ELEMENTS);
                        }
                        catch (Exception e)
                        {
                            ForwardOnError(e);
                        }
                    }
                    else
                    {
                        ForwardOnNext(_value!);
                        ForwardOnCompleted();
                    }
                }
            }
        }
    }
}

View on GitHub (pinned to 94b5d5ab91)