Cysharp/UniTask · error · InvalidOperationException

Disposable is already set

Error message

Disposable is already set

What it means

Thrown by SingleAssignmentDisposable.Disposable setter when a non-null disposable is already assigned and a different non-null value is set. SingleAssignmentDisposable is designed to hold exactly one disposable; assigning a second one before Dispose is a programming error indicating a logic bug. The old disposable is not auto-disposed — the throw is intentional to surface the misuse.

Source

Thrown at src/UniTask/Assets/Plugins/UniTask/Runtime/UniTaskObservableExtensions.cs:341

                bool alreadyDisposed;
                lock (gate)
                {
                    alreadyDisposed = disposed;
                    old = current;
                    if (!alreadyDisposed)
                    {
                        if (value == null) return;
                        current = value;
                    }
                }

                if (alreadyDisposed && value != null)
                {
                    value.Dispose();
                    return;
                }

                if (old != null) throw new InvalidOperationException("Disposable is already set");
            }
        }


        public void Dispose()
        {
            IDisposable old = null;

            lock (gate)
            {
                if (!disposed)
                {
                    disposed = true;
                    old = current;
                    current = null;
                }
            }

View on GitHub (pinned to ceac8d6946)

Solutions

  1. Use SerialAssignmentDisposable (or dispose the old one first) if you need to swap disposables
  2. Call Dispose() on the SingleAssignmentDisposable before assigning a new value
  3. Use a separate SingleAssignmentDisposable for each subscription
  4. Null-check and dispose: if (holder.Disposable != null) holder.Disposable.Dispose(); before reassigning — but prefer SerialDisposable for this pattern

Example fix

// before
var sad = new SingleAssignmentDisposable();
sad.Disposable = obs1.Subscribe(handler);
sad.Disposable = obs2.Subscribe(handler); // throws

// after (use SerialDisposable for swapping)
var sd = new SerialDisposable();
sd.Disposable = obs1.Subscribe(handler);
sd.Disposable = obs2.Subscribe(handler); // disposes obs1, ok
Defensive patterns

Strategy: validation

Validate before calling

// Before assigning, check if already set
if (holder.Disposable != null)
{
    holder.Disposable.Dispose(); // or use SerialDisposable instead
}
holder.Disposable = newSubscription;

Prevention

When it happens

Trigger: Setting the Disposable property twice on the same SingleAssignmentDisposable instance before calling Dispose. For example, subscribing twice and assigning each subscription to the same SingleAssignmentDisposable.

Common situations: Rx/Observable interop where multiple subscriptions reuse the same disposable holder. Event handler registration loops that overwrite the disposable. Refactoring that accidentally merges two subscription lifecycles into one SingleAssignmentDisposable.

Related errors


AI-assisted analysis of Cysharp/UniTask@ceac8d6946 (2026-08-13). Data as JSON: /api/errors/fb27adcc97297817. Report an issue: GitHub.