louthy/language-ext · info · OperationCanceledException

OperationCanceledException

Error message

OperationCanceledException

What it means

Iterable.Strict.AsEnumerableIO iterates the fully materialized Items list and throws OperationCanceledException if the EnvIO token is cancelled before yielding an element. Because Items is already in memory, this error only appears when the token is cancelled while the enumeration is actually running (e.g. mid-iteration). It is cooperative cancellation by design.

Solutions

  1. Catch OperationCanceledException at the enumeration site and handle shutdown
  2. Run the IO with a token that stays uncancelled during enumeration
  3. Snapshot with ToList() inside an uncancelled scope if you must have complete results
  4. Keep the CancellationTokenSource alive until enumeration completes (watch for premature Dispose)

Example fix

// before
foreach (var x in strictIterable.AsEnumerableIO().Run(EnvIO.New(token))) { ... }
// after
try
{
    foreach (var x in strictIterable.AsEnumerableIO().Run(EnvIO.New(token))) { ... }
}
catch (OperationCanceledException) { /* cancelled mid-iteration */ }
Defensive patterns

Strategy: try-catch

Validate before calling

if (env.Token.IsCancellationRequested) { /* skip enumeration */ }

Try / catch

try { foreach (var x in seq) { ... } }
catch (OperationCanceledException) { /* cancelled mid-iteration */ }

Prevention

When it happens

Trigger: Enumerating the IO<IEnumerable<A>> from a strict (already-materialized) Iterable after CancellationTokenSource.Cancel() on the EnvIO token; the check at line 29 fires before each yield return over Items.

Common situations: Cancelling during iteration of a strict collection (e.g. request aborted partway through draining a cached list); tests cancelling tokens to verify cancellation behavior; using-block disposal cancelling the token while an iterator is still live.

Related errors


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

Appendix: source

Thrown at LanguageExt.Core/Immutable Collections/Iterable/DSL/Iterable.Strict.cs:29

        false;
    
    public override IO<int> CountIO() =>
        IO.pure(Items.Count);

    public override Iterable<A> Add(A item) =>
        new IterableStrict<A>((SeqStrict<A>)Items.Add(item));

    public override Iterable<A> Cons(A item) =>
        new IterableStrict<A>((SeqStrict<A>)Items.Cons(item));

    public override IO<IEnumerable<A>> AsEnumerableIO()
    {
        return IO.lift(go);
        IEnumerable<A> go(EnvIO env)
        {
            foreach (var x in Items)
            {
                if (env.Token.IsCancellationRequested) throw new OperationCanceledException();
                yield return x;
            }
        }
    }

    public override IO<IAsyncEnumerable<A>> AsAsyncEnumerableIO()
    {
        return IO.lift(go);
        async IAsyncEnumerable<A> go(EnvIO env)
        {
            foreach (var x in Items)
            {
                if (env.Token.IsCancellationRequested) throw new OperationCanceledException();
                yield return x;
            }
        }
    }

View on GitHub (pinned to 2f0e362824)