louthy/language-ext · warning · OperationCanceledException

OperationCanceledException

Error message

OperationCanceledException

What it means

AsEnumerable creates an EnvIO from the given token and throws OperationCanceledException inside the yield loop when the token is cancelled while a consumer pulls elements. Because it is an iterator, the exception is deferred: it fires during MoveNext on the enumerable, not at the AsEnumerable call itself.

Solutions

  1. Enumerate the result promptly and pass a token whose lifetime covers the whole enumeration.
  2. Catch OperationCanceledException around the foreach that consumes the enumerable.
  3. Materialize with ToList()/ToArray() while the token is still valid if you need the data afterwards.
  4. Use the default token (CancellationToken.None) when no cancellation semantics are required.

Example fix

// before
var xs = iterable.AsEnumerable(requestAbortedToken);
SaveToDb(xs.ToList()); // enumerated later, token already cancelled
// after
var xs = iterable.AsEnumerable(requestAbortedToken).ToList(); // enumerate now
SaveToDb(xs);
Defensive patterns

Strategy: try-catch

Validate before calling

if (token.IsCancellationRequested) throw new OperationCanceledException(token); // check before enumerating

Try / catch

try
{
    foreach (var x in iterable.AsEnumerable(token))
        Process(x);
}
catch (OperationCanceledException) when (token.IsCancellationRequested)
{
    // enumeration cancelled
}

Prevention

When it happens

Trigger: Enumerating the IEnumerable returned by AsEnumerable(token) after that token is cancelled; also triggered indirectly by Choose, IterIO, EqualsIO, and iterA, all of which call AsEnumerable internally.

Common situations: LINQ pipelines that enumerate lazily after a request was aborted; holding the enumerable longer than the token's lifetime (e.g. token from a disposed request context); iterating a very large iterable past a timeout.

Related errors


AI-assisted analysis of louthy/language-ext@2f0e362824 (2026-09-15). Data as JSON: /api/errors/73b75dd2a2f85883. Report an issue: GitHub.

Appendix: source

Thrown at LanguageExt.Core/Immutable Collections/Iterable/Iterable.cs:74

        CountIO().Run();

    /// <summary>
    /// Number of items in the sequence.
    /// </summary>
    [Pure]
    public abstract IO<int> CountIO();

    /// <summary>
    /// Stream as an enumerable
    /// </summary>
    [Pure]
    public IEnumerable<A> AsEnumerable(CancellationToken token = default)
    {
        using var env = EnvIO.New(token: token);
        var       xs  = AsEnumerableIO().Run();
        foreach (var x in xs)
        {
            if(env.Token.IsCancellationRequested) throw new OperationCanceledException();
            yield return x;
        }
    }

    /// <summary>
    /// Stream as an enumerable
    /// </summary>
    [Pure]
    public abstract IO<IEnumerable<A>> AsEnumerableIO();

    /// <summary>
    /// Stream as an enumerable
    /// </summary>
    [Pure]
    public async IAsyncEnumerable<A> AsAsyncEnumerable([EnumeratorCancellation] CancellationToken token = default)
    {
        using var env = EnvIO.New(token: token);
        var xs = await AsAsyncEnumerableIO().RunAsync(env);

View on GitHub (pinned to 2f0e362824)