Cysharp/UniTask · error · InvalidOperationException
Enumerator is already running, does not allow call GetAsyncE
Error message
Enumerator is already running, does not allow call GetAsyncEnumerator twice.
What it means
Thrown by SingleConsumerUnboundedChannel's ReadAllAsyncEnumerable when GetAsyncEnumerator is called a second time. The enumerable tracks a boolean 'running' flag set on first enumeration; a second call is rejected because the channel is designed for single-consumer use. Multiple concurrent enumerators would corrupt the shared queue.
Source
Thrown at src/UniTask/Assets/Plugins/UniTask/Runtime/Channel.cs:388
CancellationToken cancellationToken2;
CancellationTokenRegistration cancellationTokenRegistration1;
CancellationTokenRegistration cancellationTokenRegistration2;
T current;
bool cacheValue;
bool running;
public ReadAllAsyncEnumerable(SingleConsumerUnboundedChannelReader parent, CancellationToken cancellationToken)
{
this.parent = parent;
this.cancellationToken1 = cancellationToken;
}
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
if (running)
{
throw new InvalidOperationException("Enumerator is already running, does not allow call GetAsyncEnumerator twice.");
}
if (this.cancellationToken1 != cancellationToken)
{
this.cancellationToken2 = cancellationToken;
}
if (this.cancellationToken1.CanBeCanceled)
{
this.cancellationTokenRegistration1 = this.cancellationToken1.RegisterWithoutCaptureExecutionContext(CancellationCallback1Delegate, this);
}
if (this.cancellationToken2.CanBeCanceled)
{
this.cancellationTokenRegistration2 = this.cancellationToken2.RegisterWithoutCaptureExecutionContext(CancellationCallback2Delegate, this);
}
running = true;View on GitHub (pinned to ceac8d6946)
Solutions
- Use only one consumer per SingleConsumerUnboundedChannel — if multiple consumers are needed, create separate channels or implement a broadcast layer.
- Create a new channel via Channel.CreateSingleConsumerUnbounded<T>() for each independent consumer.
- If you need multi-consumer fan-out, subscribe each consumer to its own channel and forward items manually.
Example fix
// before (throws on second consumer)
var enumerable = channel.Reader.ReadAllAsync();
await foreach (var x in enumerable) { }
await foreach (var y in enumerable) { } // throws
// after
var ch1 = Channel.CreateSingleConsumerUnbounded<int>();
var ch2 = Channel.CreateSingleConsumerUnbounded<int>();
// producer writes to both
await foreach (var x in ch1.Reader.ReadAllAsync()) { }
await foreach (var y in ch2.Reader.ReadAllAsync()) { } Defensive patterns
Strategy: validation
Validate before calling
// Ensure only one consumer per channel // Before enumerating, verify the channel is a fresh single-consumer channel // and you are the sole consumer. // There is no public IsEnumerating flag — design your code so each // SingleConsumerUnboundedChannel has exactly one ReadAllAsync caller.
Try / catch
try
{
await foreach (var item in channel.Reader.ReadAllAsync()) { /* ... */ }
}
catch (InvalidOperationException ex) when (ex.Message.Contains("GetAsyncEnumerator twice"))
{
// A second consumer attempted enumeration; redesign for single consumer
} Prevention
- Document and enforce that SingleConsumerUnboundedChannel has exactly one consumer.
- For fan-out scenarios, create separate channels per consumer rather than sharing one.
- Avoid breaking out of an await foreach and re-entering the same enumerable.
When it happens
Trigger: Calling 'await foreach' twice on the same IUniTaskAsyncEnumerable returned by reader.ReadAllAsync(). Or manually invoking GetAsyncEnumerator() twice on that enumerable. Or sharing the same single-consumer channel reader between two concurrent consumer tasks.
Common situations: Fan-out pattern incorrectly applied to a SingleConsumerUnboundedChannel. A test or utility that re-enumerates the same source. Code that breaks out of an await foreach loop and then tries to enumerate again.
Related errors
- Channel is already closed.
- Attempting to use an invalid operation handle
- Not yet completed.
- continuation is already registered.
- AsyncSubject is not completed yet
AI-assisted analysis of Cysharp/UniTask@ceac8d6946 (2026-08-13).
Data as JSON: /api/errors/7749128bea4c031b.
Report an issue: GitHub.