dotnet/reactive · error · InvalidOperationException
Strings_Core.DISPOSABLE_ALREADY_ASSIGNED
Error message
Strings_Core.DISPOSABLE_ALREADY_ASSIGNED
What it means
SingleAssignmentDisposableValue.Disposable may be assigned only once; a second assignment raises InvalidOperationException with the DISPOSABLE_ALREADY_ASSIGNED message. This enforces the single-assignment contract that lets the class safely swap-free storage of one disposable.
Solutions
- Create a new SingleAssignmentDisposableValue for each assignment instead of reusing one.
- Check whether it is already assigned (e.g. keep a flag or use TrySetSingle semantics) before setting.
- Switch to SerialDisposable, which allows the inner disposable to be replaced repeatedly.
Example fix
// before
subscription.Disposable = newDisposable; // second assignment -> throws
// after
subscription = new SingleAssignmentDisposableValue { Disposable = newDisposable }; Defensive patterns
Strategy: type-guard
Validate before calling
// Only assign once per instance; recreate for new subscriptions
subscription = new SingleAssignmentDisposableValue { Disposable = d }; Type guard
static bool CanAssign(SingleAssignmentDisposableValue s, IDisposable newValue) => true; // no public read; guard by design: never reassign
Try / catch
try { sabv.Disposable = d; }
catch (InvalidOperationException) { /* already assigned: create a new instance or ignore duplicate assign */ } Prevention
- Use SerialDisposable when the inner disposable must be replaceable.
- Never reuse a SingleAssignmentDisposableValue across subscriptions; allocate per assignment.
- Guard initialization paths against double execution (e.g. Interlocked flags).
When it happens
Trigger: Setting the Disposable property a second time with a different value after an initial assignment — e.g. re-using a SingleAssignmentDisposableValue field for a second subscription or re-running an initialization path.
Common situations: Re-subscribing with the same field without recreating the wrapper, event handlers firing initialization twice, retry loops that assign a new disposable into the same field, or accidental shared instances between two call sites.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Value cannot be null. (Parameter 'disposable')
- RefCountDisposable
- RefCountDisposable can't handle more than 2147483647…
- Value cannot be null. (Parameter 'disposable')
- Value cannot be null. (Parameter 'disposable1')
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/1db79314677da11d.
Report an issue: GitHub.
Appendix: source
Thrown at Rx.NET/Source/src/System.Reactive/Disposables/SingleAssignmentDisposableValue.cs:39
// We use a sentinel value to indicate we've been disposed. This sentinel never leaks
// to the outside world (see the Disposable property getter), so no-one can ever assign
// this value to us manually.
Volatile.Read(ref _current) == BooleanDisposable.True;
/// <summary>
/// Gets or sets the underlying disposable. After disposal, the result of getting this property is undefined.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if the <see cref="SingleAssignmentDisposable"/> has already been assigned to.</exception>
public IDisposable? Disposable
{
get => Disposables.Disposable.GetValueOrDefault(ref _current);
set
{
var result = Disposables.Disposable.TrySetSingle(ref _current, value);
if (result == TrySetSingleResult.AlreadyAssigned)
{
throw new InvalidOperationException(Strings_Core.DISPOSABLE_ALREADY_ASSIGNED);
}
}
}
/// <summary>
/// Disposes the underlying disposable.
/// </summary>
public void Dispose()
{
Disposables.Disposable.Dispose(ref _current);
}
/// <inheritdoc/>
public override readonly bool Equals(object? obj) => false;
/// <inheritdoc/>
public override readonly int GetHashCode() => 0;
View on GitHub (pinned to 94b5d5ab91)