dotnet/reactive · error · ArgumentNullException
Value cannot be null. (Parameter 'gate')
Error message
Value cannot be null. (Parameter 'gate')
What it means
System.Reactive's Observer.Synchronize<T>(IObserver<T>, object gate) throws ArgumentNullException when the gate object is null. The gate is the lock object used to serialize OnNext/OnError/OnCompleted calls to the wrapped observer, so it is mandatory. The library fails fast at the extension-method boundary rather than throwing later from inside SynchronizedObserver.
Solutions
- Pass a non-null lock object, e.g. new object(), as the gate
- Ensure the field holding the gate is initialized before the Synchronize call (initialize inline: readonly object _gate = new object();)
- If you meant the AsyncLock overload, pass a new AsyncLock() instead
- Add a null check or require a non-null gate at the caller's API boundary
Example fix
// before object gate = GetLock(); // returns null var synced = Observer.Synchronize(observer, gate); // after var gate = new object(); var synced = Observer.Synchronize(observer, gate);
Defensive patterns
Strategy: validation
Validate before calling
if (observer is null) throw new ArgumentNullException(nameof(observer)); if (gate is null) gate = new object(); var synced = Observer.Synchronize(observer, gate);
Type guard
static bool IsValidGate(object? gate) => gate is not null;
Try / catch
try { var synced = Observer.Synchronize(observer, gate); }
catch (ArgumentNullException ex) when (ex.ParamName == "gate")
{
gate = new object();
var synced = Observer.Synchronize(observer, gate);
} Prevention
- Initialize lock fields inline: private readonly object _gate = new object();
- Never leave lock references nullable if they are always required
- Prefer the AsyncLock overload when you already have an AsyncLock instance
- Assert gate non-null in unit tests for subscription setup code
When it happens
Trigger: Calling Observer.Synchronize(observer, gate) with a null second argument — e.g. passing an uninitialized lock field, a null returned from a factory, or accidentally calling the (observer, gate) overload when intending the (observer, AsyncLock) overload with a null AsyncLock.
Common situations: A lock object initialized lazily but used before initialization; refactoring that removed the lock allocation; confusion between the object-gate and AsyncLock overloads after a null asyncLock propagates from dependency injection.
Related errors
- predicate
- Value cannot be null. (Parameter 'source5')
- Value cannot be null. (Parameter 'asyncLock')
- Value cannot be null. (Parameter 'source')
- Value cannot be null. (Parameter 'scheduler')
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/b21aee2c400b239f.
Report an issue: GitHub.
Appendix: source
Thrown at Rx.NET/Source/src/System.Reactive/Observer.Extensions.cs:257
/// <param name="observer">The observer whose callbacks should be synchronized.</param>
/// <param name="gate">Gate object to synchronize each observer call on.</param>
/// <returns>An observer that delivers callbacks to the specified observer in a synchronized manner.</returns>
/// <exception cref="ArgumentNullException"><paramref name="observer"/> or <paramref name="gate"/> is null.</exception>
/// <remarks>
/// Because a <see cref="Monitor">Monitor</see> is used to perform the synchronization, there's no protection against reentrancy from the same thread.
/// Hence, overlapped observer callbacks are still possible, which is invalid behavior according to the observer grammar. In order to protect against this behavior as
/// well, use the <see cref="Synchronize{T}(IObserver{T}, AsyncLock)"/> overload.
/// </remarks>
public static IObserver<T> Synchronize<T>(IObserver<T> observer, object gate)
{
if (observer == null)
{
throw new ArgumentNullException(nameof(observer));
}
if (gate == null)
{
throw new ArgumentNullException(nameof(gate));
}
return new SynchronizedObserver<T>(observer, gate);
}
/// <summary>
/// Synchronizes access to the observer such that its callback methods cannot be called concurrently, using the specified asynchronous lock to protect against concurrent and reentrant access.
/// This overload is useful when coordinating multiple observers that access shared state by synchronizing on a common asynchronous lock.
/// </summary>
/// <typeparam name="T">The type of the elements received by the source observer.</typeparam>
/// <param name="observer">The observer whose callbacks should be synchronized.</param>
/// <param name="asyncLock">Gate object to synchronize each observer call on.</param>
/// <returns>An observer that delivers callbacks to the specified observer in a synchronized manner.</returns>
/// <exception cref="ArgumentNullException"><paramref name="observer"/> or <paramref name="asyncLock"/> is null.</exception>
public static IObserver<T> Synchronize<T>(IObserver<T> observer, AsyncLock asyncLock)
{
if (observer == null)
{View on GitHub (pinned to 94b5d5ab91)