dotnet/reactive · error · InvalidOperationException
The scheduler operation has already been awaited.
Error message
The scheduler operation has already been awaited.
What it means
A SchedulerOperation can only be awaited once: OnCompleted stores the continuation and throws InvalidOperationException ('The scheduler operation has already been awaited') on a second registration. This protects the single-continuation awaitable contract.
Solutions
- Await each SchedulerOperation exactly once; create a new operation for each await
- If retrying, re-issue the scheduled operation instead of re-awaiting the old one
- Capture the result via Task-like semantics only once and share the resulting Task instead
Example fix
// before var op = scheduler.ScheduleAsync(...); await op; await op; // second await throws // after var op = scheduler.ScheduleAsync(...); await op; var op2 = scheduler.ScheduleAsync(...); // new operation for second await await op2;
Defensive patterns
Strategy: try-catch
Try / catch
try { await operation; }
catch (InvalidOperationException) { /* already awaited: treat as no-op */ } Prevention
- Await each SchedulerOperation exactly once
- Store a Task/awaited result instead of the operation for reuse
- In retry loops, create a fresh operation per attempt
- Track awaited state if wrapping the operation in your own type
When it happens
Trigger: Awaiting the same SchedulerOperation instance twice; calling OnCompleted again after a previous await completed or was cancelled; storing the operation and re-awaiting on retry logic.
Common situations: Retry loops that reuse the awaitable instead of obtaining a new operation; awaiting in both the original async method and a timeout/abandonment helper; caching the task-like object.
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
- AdvanceBy cannot be called when the scheduler is already…
- scheduler (Parameter 'scheduler')
- Strings_Core.HEAP_EMPTY
- Value cannot be null. (Parameter 'continuation')
- action
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/178cc7e12d254745.
Report an issue: GitHub.
Appendix: source
Thrown at Rx.NET/Source/src/System.Reactive/Concurrency/SchedulerOperation.cs:98
/// <summary>
/// Completes the scheduler operation, throwing an OperationCanceledException in case cancellation was requested.
/// </summary>
public void GetResult() => _cancellationToken.ThrowIfCancellationRequested();
/// <summary>
/// Registers the continuation with the scheduler operation.
/// </summary>
/// <param name="continuation">Continuation to be run on the originating scheduler.</param>
public void OnCompleted(Action continuation)
{
if (continuation == null)
{
throw new ArgumentNullException(nameof(continuation));
}
if (_continuation != null)
{
throw new InvalidOperationException(Strings_Core.SCHEDULER_OPERATION_ALREADY_AWAITED);
}
if (_postBackToOriginalContext)
{
var ctx = SynchronizationContext.Current;
if (ctx != null)
{
var original = continuation;
continuation = () =>
{
//
// No need for OperationStarted and OperationCompleted calls here;
// this code is invoked through await support and will have a way
// to observe its start/complete behavior, either through returned
// Task objects or the async method builder's interaction with the
// SynchronizationContext object.
//
// In general though, Rx doesn't play nicely with synchronizationView on GitHub (pinned to 94b5d5ab91)