dotnet/reactive · error · ArgumentException

Expected Qbservable.ToQueryable.

Error message

Expected Qbservable.ToQueryable.

What it means

CreateQuery on the IQbservable<T>->IQueryable bridge provider only accepts expression trees whose single method call is Qbservable.ToQueryable. Any other expression shape or method is rejected with ArgumentException because the provider cannot translate it back to an IQbservable pipeline. This is a guard for an internal conversion pattern (observable.AsQbservable().<operators>.ToEnumerable().AsQueryable()).

Solutions

  1. Only call CreateQuery with the result of an AsQbservable().ToQueryable() expression tree; use observable.AsQbservable() and operators on IQbservable directly instead of Queryable operators
  2. Check the expression before passing: it must be a MethodCallExpression with Method.DeclaringType == typeof(Qbservable) and Method.Name == nameof(Qbservable.ToQueryable)
  3. Perform filtering/transformation on the IQbservable<T> side before converting to IQueryable

Example fix

// before
IQueryable<int> q = provider.CreateQuery(Expression.Call(typeof(Queryable), "Where", new[]{typeof(int)}, source.Expression, predicate));
// after
IQueryable<int> q = source.AsQbservable().Where(x => x > 0).ToEnumerable().AsQueryable();
Defensive patterns

Strategy: validation

Validate before calling

if (expr is not MethodCallExpression mc || mc.Method.DeclaringType != typeof(Qbservable) || mc.Method.Name != nameof(Qbservable.ToQueryable)) throw new ArgumentException("Only Qbservable.ToQueryable expressions are supported", nameof(expr));

Type guard

static bool IsToQueryableCall(Expression e) => e is MethodCallExpression c && c.Method.DeclaringType == typeof(Qbservable) && c.Method.Name == nameof(Qbservable.ToQueryable);

Prevention

When it happens

Trigger: Calling IQueryProvider.CreateQuery with an expression that is not a MethodCallExpression, whose declaring type is not Qbservable, or whose method name is not ToQueryable — e.g. composing Queryable operators directly on the queryable without going through AsQbservable().

Common situations: Developers manually building expression trees against an ExpressionViewModel/IQueryable view of an IQbservable, or using LINQ query syntax over the wrapped queryable with operators the bridge does not recognize.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/ObservableQuery.cs:53

        IQueryable<TElement> IQueryProvider.CreateQuery<TElement>(Expression expression)
        {
            //
            // Here we're on the edge between IQbservable and IQueryable for the local
            // execution case. E.g.:
            //
            //   observable.AsQbservable().<operators>.ToQueryable()
            //
            // This should be turned into a local execution, with the push-to-pull
            // adapter in the middle, so we rewrite to:
            //
            //   observable.AsQbservable().<operators>.ToEnumerable().AsQueryable()
            //
            if (expression is not MethodCallExpression call ||
                call.Method.DeclaringType != typeof(Qbservable) ||
                call.Method.Name != nameof(Qbservable.ToQueryable))
            {
                throw new ArgumentException(Strings_Providers.EXPECTED_TOQUERYABLE_METHODCALL, nameof(expression));
            }

            //
            // This is the IQbservable<T> object corresponding to the lhs. Now wrap
            // it in two calls to get the local queryable.
            //
            var arg0 = call.Arguments[0];
            var res =
                Expression.Call(
                    AsQueryable.MakeGenericMethod(typeof(TElement)),
                    Expression.Call(
                        typeof(Observable).GetMethod(nameof(Observable.ToEnumerable))!.MakeGenericMethod(typeof(TElement)),
                        arg0
                    )
                );

            //
            // Queryable operator calls should be taken care of by the provider for

View on GitHub (pinned to 94b5d5ab91)