dotnet/reactive · error · ArgumentNullException

end

Error message

end

What it means

The single-argument FromAsyncPattern<TArg1,TResult> overload throws ArgumentNullException('end') when the end delegate Func<IAsyncResult, TResult> is null. Both begin and end delegates are mandatory: end converts the IAsyncResult into the final result and completes the observable sequence. Validation happens when the wrapper Func is created.

Solutions

  1. Supply the EndXxx method (or a lambda wrapping it) as the end delegate
  2. Null-check delegates obtained dynamically before passing them
  3. Use Observable.FromAsync with a Task-returning function instead of APM

Example fix

// before
var f = Observable.FromAsyncPattern<int, byte[]>(begin, null);
// after
var f = Observable.FromAsyncPattern<int, byte[]>(begin, iar => stream.EndRead(iar));
Defensive patterns

Strategy: validation

Validate before calling

if (begin == null || end == null) throw new InvalidOperationException("Both begin and end delegates are required");
var f = Observable.FromAsyncPattern<TArg1, TResult>(begin, end);

Type guard

static bool ValidApmPair<TArg1,TResult>(Func<TArg1, AsyncCallback, object?, IAsyncResult> b, Func<IAsyncResult, TResult> e) => b != null && e != null;

Try / catch

try
{
    var f = Observable.FromAsyncPattern<TArg1, TResult>(begin, end);
}
catch (ArgumentNullException ex) when (ex.ParamName == "end")
{
    // supply the end delegate
}

Prevention

When it happens

Trigger: Observable.FromAsyncPattern<TArg1,TResult>(begin, null) - the end argument omitted or resolved to null.

Common situations: Wrapping legacy Begin/End pairs where only the Begin call was ported; conditional wiring code that leaves end unassigned; wrong generic type inference pushing developers toward the wrong overload.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Linq/Observable.Async.cs:62

        /// </summary>
        /// <typeparam name="TArg1">The type of the first argument passed to the begin delegate.</typeparam>
        /// <typeparam name="TResult">The type of the result returned by the end delegate.</typeparam>
        /// <param name="begin">The delegate that begins the asynchronous operation.</param>
        /// <param name="end">The delegate that ends the asynchronous operation.</param>
        /// <returns>Function that can be used to start the asynchronous operation and retrieve the result as an observable sequence.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="begin"/> or <paramref name="end"/> is null.</exception>
        /// <remarks>Each invocation of the resulting function will cause the asynchronous operation to be started. Subscription to the resulting sequence has no observable side-effect, and each subscription will produce the asynchronous operation's result.</remarks>
        [Obsolete(Constants_Linq.UseTaskFromAsyncPattern)]
        public static Func<TArg1, IObservable<TResult>> FromAsyncPattern<TArg1, TResult>(Func<TArg1, AsyncCallback, object?, IAsyncResult> begin, Func<IAsyncResult, TResult> end)
        {
            if (begin == null)
            {
                throw new ArgumentNullException(nameof(begin));
            }

            if (end == null)
            {
                throw new ArgumentNullException(nameof(end));
            }

            return s_impl.FromAsyncPattern(begin, end);
        }

        /// <summary>
        /// Converts a Begin/End invoke function pair into an asynchronous function.
        /// </summary>
        /// <typeparam name="TArg1">The type of the first argument passed to the begin delegate.</typeparam>
        /// <typeparam name="TArg2">The type of the second argument passed to the begin delegate.</typeparam>
        /// <typeparam name="TResult">The type of the result returned by the end delegate.</typeparam>
        /// <param name="begin">The delegate that begins the asynchronous operation.</param>
        /// <param name="end">The delegate that ends the asynchronous operation.</param>
        /// <returns>Function that can be used to start the asynchronous operation and retrieve the result as an observable sequence.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="begin"/> or <paramref name="end"/> is null.</exception>
        /// <remarks>Each invocation of the resulting function will cause the asynchronous operation to be started. Subscription to the resulting sequence has no observable side-effect, and each subscription will produce the asynchronous operation's result.</remarks>
        [Obsolete(Constants_Linq.UseTaskFromAsyncPattern)]
        public static Func<TArg1, TArg2, IObservable<TResult>> FromAsyncPattern<TArg1, TArg2, TResult>(Func<TArg1, TArg2, AsyncCallback, object?, IAsyncResult> begin, Func<IAsyncResult, TResult> end)

View on GitHub (pinned to 94b5d5ab91)