louthy/language-ext · warning · OperationCanceledException
OperationCanceledException
Error message
OperationCanceledException
What it means
ObservableExt's buffering/consume loop rethrows cancellation: when a queued item is a Fail whose error is Errors.Cancelled, the iterating coroutine throws OperationCanceledException. This signals that the underlying async stream/token was cancelled rather than a normal error.
Solutions
- Catch OperationCanceledException around enumeration and treat it as normal cancellation.
- Check the CancellationToken.IsCancellationRequested before/while consuming.
- Pass the token through so cancellation is cooperative rather than exception-based.
Example fix
// before
foreach (var x in observableExt) Process(x);
// after
try { foreach (var x in observableExt) Process(x); }
catch (OperationCanceledException) { /* expected on shutdown */ } Defensive patterns
Strategy: try-catch
Validate before calling
if (token.IsCancellationRequested) return; // check before enumerating
Try / catch
try { foreach (var x in obs) { } } catch (OperationCanceledException) { /* cancelled: expected */ } Prevention
- Pass CancellationToken through the whole pipeline
- Handle Errors.Cancelled distinctly from real failures
- Treat OperationCanceledException as control flow, not failure
When it happens
Trigger: Iterating the observable extension while the source completes with Errors.Cancelled (cancellation token fired upstream), e.g. via a CancellationToken passed to the producing async operation.
Common situations: User-initiated cancellation of long-running observable pipelines, host shutdown, or timeout tokens firing while an observable consumer is mid-enumeration.
Related errors
- OperationCanceledException
- OperationCanceledException
- OperationCanceledException
- OperationCanceledException
- OperationCanceledException
AI-assisted analysis of louthy/language-ext@2f0e362824 (2026-09-15).
Data as JSON: /api/errors/03f1ecfe57e687d4.
Report an issue: GitHub.
Appendix: source
Thrown at LanguageExt.Core/Extensions/ObservableExt.cs:71
}
public static async IAsyncEnumerable<A> Run(
IObservable<A> observable,
[EnumeratorCancellation] CancellationToken token)
{
using var wait = new AutoResetEvent(false);
var queue = new ConcurrentQueue<Fin<A>>();
observable.Subscribe(new Observe<A>(wait, queue));
while (true)
{
await wait.WaitOneAsync(token).ConfigureAwait(false);
while (queue.TryDequeue(out var item))
{
if (item.IsFail)
{
if (item.FailValue == Errors.None) yield break;
if (item.FailValue == Errors.Cancelled) throw new OperationCanceledException();
item.FailValue.Throw();
yield break;
}
else
{
yield return item.SuccValue;
}
}
}
}
public void OnCompleted()
{
queue.Enqueue(Errors.None);
wait.Set();
}
public void OnError(Exception error)View on GitHub (pinned to 2f0e362824)