dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'value')

Error message

Value cannot be null. (Parameter 'value')

What it means

PlatformEnlightenmentProvider.Current is the static injection point for Rx's platform enlightenment provider. Its setter rejects null with ArgumentNullException('value') because a null provider would break every internal enlightenment lookup (concurrency, timers, etc.).

Solutions

  1. Assign a valid IPlatformEnlightenmentProvider instance (e.g. new CurrentPlatformEnlightenmentProvider() or the test provider).
  2. Skip the assignment when the resolved value is null instead of writing it back.
  3. Cache the original provider and restore that reference, never null.

Example fix

// before
PlatformEnlightenmentProvider.Current = ResolveProvider();
// after
var provider = ResolveProvider();
if (provider != null) { PlatformEnlightenmentProvider.Current = provider; }
Defensive patterns

Strategy: validation

Validate before calling

if (provider != null) { PlatformEnlightenmentProvider.Current = provider; }

Type guard

bool IsValidProvider(IPlatformEnlightenmentProvider? p) => p is not null;

Try / catch

try { PlatformEnlightenmentProvider.Current = resolved; } catch (ArgumentNullException ex) when (ex.ParamName == "value") { /* fall back to default provider */ }

Prevention

When it happens

Trigger: Assigning PlatformEnlightenmentProvider.Current = null, typically in test teardown or DI setup code that resets a stored provider reference which was never initialized.

Common situations: Unit-test fixtures clearing static state between tests; host applications that read the provider from a service locator returning null before assignment.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Internal/PlatformEnlightenmentProvider.cs:58

        // NOTE TO MAINTAINERS
        //
        //  Do *NOT* remove this mechanism which has been used beyond its original goal of supporting dependency injection when we were unifying
        //  the different Rx implementations as one Portable Library with platform-specific PlatformServices assemblies on top. Besides this, it
        //  has been used (and is still being used) for hosting scenarios where the host wants control over thread creation (cf. CAL), or other
        //  services (e.g. interception of query operators for logging, debugging, quota management, policy injection, etc.). Compat matters and
        //  lack thereof results in users getting stuck on older versions or having to resort to forking.
        //

        /// <summary>
        /// (Infrastructure) Gets the current enlightenment provider. If none is loaded yet, accessing this property triggers provider resolution.
        /// </summary>
        /// <remarks>
        /// This member is used by the Rx infrastructure and not meant for public consumption or implementation.
        /// </remarks>
        public static IPlatformEnlightenmentProvider Current
        {
            get => _current;
            set => _current = value ?? throw new ArgumentNullException(nameof(value));
        }

        private static IPlatformEnlightenmentProvider CreatePlatformProvider() => new CurrentPlatformEnlightenmentProvider();
    }
}

View on GitHub (pinned to 94b5d5ab91)