{"record":{"id":"539b6fb0d65497d3","repo":"louthy/language-ext","slug":"operationcanceledexception-iterable-asyncenumerable","errorCode":null,"errorMessage":"OperationCanceledException","messagePattern":"OperationCanceledException","errorType":"exception","errorClass":"OperationCanceledException","httpStatus":null,"severity":"info","filePath":"LanguageExt.Core/Immutable Collections/Iterable/DSL/Iterable.AsyncEnumerable.cs","lineNumber":28,"sourceCode":"\nsealed class IterableAsyncEnumerable<A>(IO<IAsyncEnumerable<A>> runEnumerable) : Iterable<A>\n{\n    internal override bool IsAsync =>\n        true;\n\n    public override IO<int> CountIO() =>\n        IO.liftVAsync(async env => await (await runEnumerable.RunAsync(env)).CountAsync(env.Token));\n\n    public override IO<IEnumerable<A>> AsEnumerableIO()\n    {\n        return IO.lift(env => go(env, runEnumerable));\n\n        static IEnumerable<A> go(EnvIO env, IO<IAsyncEnumerable<A>> run)\n        {\n            var xs = run.Run(env);\n            foreach (var x in xs.ToBlockingEnumerable(env.Token))\n            {\n                if (env.Token.IsCancellationRequested) throw new OperationCanceledException();\n                yield return x;\n            }\n        }\n    }\n\n    public override IO<IAsyncEnumerable<A>> AsAsyncEnumerableIO() \n    {\n        return IO.lift(env => go(env, runEnumerable));\n\n        static async IAsyncEnumerable<A> go(EnvIO env, IO<IAsyncEnumerable<A>> run)\n        {\n            var xs = await run.RunAsync(env);\n            await foreach (var x in xs.WithCancellation(env.Token))\n            {\n                yield return x;\n            }\n        }\n    }","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/louthy/language-ext/blob/2f0e3628242889774d4141960a35671a0280051f/LanguageExt.Core/Immutable Collections/Iterable/DSL/Iterable.AsyncEnumerable.cs#L10-L46","documentation":"Iterable.AsyncEnumerable.AsEnumerableIO converts an IO<IAsyncEnumerable<A>> into a synchronous IEnumerable<A> by blocking on ToBlockingEnumerable with the environment token. If that token is cancelled, both ToBlockingEnumerable and the explicit per-element check throw OperationCanceledException. The library surfaces cancellation this way so callers can abort a blocking enumeration deterministically.","triggerScenarios":"Calling AsEnumerableIO() on an Iterable whose underlying source is an IAsyncEnumerable<A>, then enumerating the returned sequence after the EnvIO token was cancelled (cts.Cancel(), timeout expiry, linked token fired); the check fires before each yield return at line 28.","commonSituations":"Bridging async streams to sync code (unit tests, legacy callers) with a test timeout cancelling the token; host shutdown mid-drain; using CancellationTokenSource.CreateLinkedTokenSource with a deadline that elapses during a slow async producer.","solutions":["Catch OperationCanceledException around the enumeration and treat it as graceful shutdown","Increase or remove the timeout on the CancellationTokenSource feeding EnvIO.New","Consume the source asynchronously with AsAsyncEnumerableIO() and await foreach instead of blocking","Ensure the async producer completes or yields control so the blocking drain is not starved while the token stays alive"],"exampleFix":"// before\nforeach (var x in iterable.AsEnumerableIO().Run(env)) { ... }\n// after\ntry\n{\n    foreach (var x in iterable.AsEnumerableIO().Run(env)) { ... }\n}\ncatch (OperationCanceledException) when (env.Token.IsCancellationRequested)\n{\n    // cancellation requested: exit cleanly\n}","handlingStrategy":"try-catch","validationCode":"if (env.Token.IsCancellationRequested) return; // don't start a blocking drain on a cancelled token","typeGuard":null,"tryCatchPattern":"try { foreach (var x in seq) { ... } }\ncatch (OperationCanceledException) when (token.IsCancellationRequested) { /* cancelled */ }","preventionTips":["Prefer async consumption (await foreach over AsAsyncEnumerableIO) over blocking ToBlockingEnumerable","Avoid bridging async sources to sync code with short-lived cancellation tokens","Set token timeouts generously relative to the slowest possible producer"],"tags":["cancellation","async","ienumerable","blocking"],"backgroundTag":"operation-cancelled","analyzedSha":"2f0e3628242889774d4141960a35671a0280051f","analyzedAt":"2026-09-15T03:31:55.716Z","contentChangedAt":"2026-09-15T03:31:55.716Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}