Cysharp/UniTask · error · InvalidOperationException
AsyncSubject is not completed yet
Error message
AsyncSubject is not completed yet
What it means
Thrown by the AsyncSubject<T>.Value getter (line 380) when isStopped is false. UniTask returns an internal AsyncSubject<T> from UniTask<T>.ToObservable() as the Rx bridge; like the Rx AsyncSubject it caches only the last value and publishes it on completion, so reading Value before the source task has finished is a contract violation (InvalidOperationException). The subject only becomes readable after Fire() calls OnNext + OnCompleted.
Source
Thrown at src/UniTask/Assets/Plugins/UniTask/Runtime/UniTaskObservableExtensions.cs:380
}
internal sealed class AsyncSubject<T> : IObservable<T>, IObserver<T>
{
object observerLock = new object();
T lastValue;
bool hasValue;
bool isStopped;
bool isDisposed;
Exception lastError;
IObserver<T> outObserver = EmptyObserver<T>.Instance;
public T Value
{
get
{
ThrowIfDisposed();
if (!isStopped) throw new InvalidOperationException("AsyncSubject is not completed yet");
if (lastError != null) ExceptionDispatchInfo.Capture(lastError).Throw();
return lastValue;
}
}
public bool HasObservers
{
get
{
return !(outObserver is EmptyObserver<T>) && !isStopped && !isDisposed;
}
}
public bool IsCompleted { get { return isStopped; } }
public void OnCompleted()
{
IObserver<T> old;View on GitHub (pinned to ceac8d6946)
Solutions
- Check subject.IsCompleted before reading .Value (the getter only returns once isStopped is true).
- Await the original UniTask<T> directly instead of reading the observable's Value.
- Use .Subscribe(observer) or .ToUniTask() to receive the value asynchronously rather than reading it eagerly.
Example fix
// before var obs = myTask.ToObservable(); var v = ((AsyncSubject<int>)obs).Value; // throws: task not done // after var v = await myTask; // await the source directly
Defensive patterns
Strategy: validation
Validate before calling
// AsyncSubject<T>.IsCompleted maps to isStopped; check before reading Value
var subject = (AsyncSubject<int>)myTask.ToObservable();
if (subject.IsCompleted) {
var v = subject.Value;
} else {
// not ready; await the source instead
} Try / catch
try {
var v = subject.Value;
} catch (InvalidOperationException) {
// AsyncSubject not completed yet; await the source instead
} Prevention
- Prefer awaiting the original UniTask<T> over reading the AsyncSubject's Value.
- Always gate .Value access behind an IsCompleted check.
- Treat ToObservable() output as a stream to subscribe to, not a synchronous value box.
When it happens
Trigger: Accessing the .Value property of the observable returned by someUniTask.ToObservable() before the underlying UniTask<T> has awaited to completion (i.e. isStopped still false). Happens when code casts the IObservable back to AsyncSubject<T> (or reaches the getter) and reads it synchronously while the producing task is still in flight.
Common situations: Polling a converted observable's value from a non-async/synchronous caller; a race where the producing task is slow and Value is read immediately after ToObservable() returns; mistaking the IObservable for an already-resolved value box.
Related errors
- error
- Disposable is already set
- observer
- Channel is already closed.
- Enumerator is already running, does not allow call GetAsyncE
AI-assisted analysis of Cysharp/UniTask@ceac8d6946 (2026-08-13).
Data as JSON: /api/errors/152b9126086ef592.
Report an issue: GitHub.