dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'end')

Error message

Value cannot be null. (Parameter 'end')

What it means

Observable.FromAsyncPattern validates its begin/end delegate arguments up front and throws ArgumentNullException('end') when the end (IAsyncResult result selector) delegate is null. The APM Begin/End pattern requires both delegates to wrap an async operation into an IObservable, so a null end delegate cannot produce a valid sequence. The library throws eagerly at wrapper creation time rather than at subscription time.

Solutions

  1. Ensure a non-null end delegate Func<IAsyncResult, TResult> is passed as the second argument
  2. Check that any reflection/config lookup producing the end delegate succeeded before calling FromAsyncPattern
  3. Prefer the modern Task-based FromAsync pattern (Observable.FromAsync of a Func<Task<T>>) which avoids Begin/End pairs entirely

Example fix

// before
var end = (Func<IAsyncResult, string>)null;
var f = Observable.FromAsyncPattern<string>(begin, end);
// after
var end = iar => ((FileStream)iar.AsyncState).EndRead(iar) is var n ? Encoding.UTF8.GetString(n == 0 ? Array.Empty<byte>() : buffer, 0, n) : null;
var f = Observable.FromAsyncPattern<string>(begin, end);
Defensive patterns

Strategy: validation

Validate before calling

if (end == null) throw new InvalidOperationException("FromAsyncPattern requires a non-null end delegate");
var f = Observable.FromAsyncPattern<TResult>(begin, end);

Type guard

static bool HasBeginEnd<TArg,TResult>(Func<TArg, AsyncCallback, object?, IAsyncResult> begin, Func<IAsyncResult, TResult> end) => begin != null && end != null;

Try / catch

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

Prevention

When it happens

Trigger: Calling the parameterless Observable.FromAsyncPattern(begin, end) overload with a non-null begin delegate but a null end delegate, e.g. FromAsyncPattern(begun, null) or passing a variable that was never assigned.

Common situations: Converting legacy Stream/WebRequest BeginXxx/EndXxx pairs to Rx; the End delegate is looked up via reflection or config and the lookup returns null; refactoring removed the end method but the call site still compiles because the parameter accepts any Func.

Related errors


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

Appendix: source

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

        /// Converts a Begin/End invoke function pair into an asynchronous function.
        /// </summary>
        /// <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<IObservable<TResult>> FromAsyncPattern<TResult>(Func<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="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)
        {

View on GitHub (pinned to 94b5d5ab91)