dotnet/reactive · error · OverflowException
RefCountDisposable can't handle more than 2147483647…
Error message
RefCountDisposable can't handle more than 2147483647 disposables
What it means
RefCountDisposable packs an active-child counter into bits 0..30 of an int (bit 31 is the disposed flag), so it can hand out at most int.MaxValue inner disposables. GetDisposable throws OverflowException when 2147483647 inner disposables are already outstanding.
Solutions
- Dispose inner disposables returned by GetDisposable as soon as they are no longer needed.
- Track outstanding leases with a bounded cap or semaphore to prevent runaway acquisition.
- If legitimately unbounded, replace with a custom counting scheme using a long counter or SplitDispose-free design.
Example fix
// before
while (running) leases.Add(refCount.GetDisposable()); // never released
// after
while (running)
{
var d = refCount.GetDisposable();
try { /* work */ }
finally { d.Dispose(); }
} Defensive patterns
Strategy: validation
Validate before calling
// Cap outstanding leases
if (Interlocked.Increment(ref outstanding) > MaxLeases)
throw new InvalidOperationException("Too many outstanding inner disposables");
var d = refCount.GetDisposable(); Try / catch
try { var d = refCount.GetDisposable(); }
catch (OverflowException) { /* back off, force disposal of outstanding leases */ } Prevention
- Always dispose inner disposables promptly (using/finally).
- Bound the number of concurrent leases with a semaphore or counter.
- Monitor outstanding-lease counts in production to detect leaks before overflow.
When it happens
Trigger: Calling GetDisposable() 2,147,483,648 times on a single RefCountDisposable without any of the returned inner disposables being disposed — usually only via a stress/leak loop.
Common situations: Unbounded subscription/lease creation in a hot loop where inner disposables are never disposed, effectively a leak that exhausts the 31-bit counter; extremely rare outside synthetic benchmarks.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- Value cannot be null. (Parameter 'disposable')
- RefCountDisposable
- Value cannot be null. (Parameter 'disposable')
- Strings_Core.DISPOSABLE_ALREADY_ASSIGNED
- Value cannot be null. (Parameter 'disposable1')
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/e96d2b23ee515ad1.
Report an issue: GitHub.
Appendix: source
Thrown at Rx.NET/Source/src/System.Reactive/Disposables/RefCountDisposable.cs:78
var cnt = Volatile.Read(ref _count);
for (; ; )
{
// If bit 31 is set and the active count is zero, don't create an inner
if (cnt == int.MinValue)
{
if (_throwWhenDisposed)
{
throw new ObjectDisposedException("RefCountDisposable");
}
return Disposable.Empty;
}
// Should not overflow the bits 0..30
if ((cnt & 0x7FFFFFFF) == int.MaxValue)
{
throw new OverflowException($"RefCountDisposable can't handle more than {int.MaxValue} disposables");
}
// Increment the active count by one, works because the increment
// won't affect bit 31
var u = Interlocked.CompareExchange(ref _count, cnt + 1, cnt);
if (u == cnt)
{
return new InnerDisposable(this);
}
cnt = u;
}
}
/// <summary>
/// Disposes the underlying disposable only when all dependent disposables have been disposed.
/// </summary>
public void Dispose()
{View on GitHub (pinned to 94b5d5ab91)