{"record":{"id":"73b75dd2a2f85883","repo":"louthy/language-ext","slug":"operationcanceledexception-iterable","errorCode":null,"errorMessage":"OperationCanceledException","messagePattern":"OperationCanceledException","errorType":"exception","errorClass":"OperationCanceledException","httpStatus":null,"severity":"warning","filePath":"LanguageExt.Core/Immutable Collections/Iterable/Iterable.cs","lineNumber":74,"sourceCode":"        CountIO().Run();\n\n    /// <summary>\n    /// Number of items in the sequence.\n    /// </summary>\n    [Pure]\n    public abstract IO<int> CountIO();\n\n    /// <summary>\n    /// Stream as an enumerable\n    /// </summary>\n    [Pure]\n    public IEnumerable<A> AsEnumerable(CancellationToken token = default)\n    {\n        using var env = EnvIO.New(token: token);\n        var       xs  = AsEnumerableIO().Run();\n        foreach (var x in xs)\n        {\n            if(env.Token.IsCancellationRequested) throw new OperationCanceledException();\n            yield return x;\n        }\n    }\n\n    /// <summary>\n    /// Stream as an enumerable\n    /// </summary>\n    [Pure]\n    public abstract IO<IEnumerable<A>> AsEnumerableIO();\n\n    /// <summary>\n    /// Stream as an enumerable\n    /// </summary>\n    [Pure]\n    public async IAsyncEnumerable<A> AsAsyncEnumerable([EnumeratorCancellation] CancellationToken token = default)\n    {\n        using var env = EnvIO.New(token: token);\n        var xs = await AsAsyncEnumerableIO().RunAsync(env);","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/louthy/language-ext/blob/2f0e3628242889774d4141960a35671a0280051f/LanguageExt.Core/Immutable Collections/Iterable/Iterable.cs#L56-L92","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Enumerate the result promptly and pass a token whose lifetime covers the whole enumeration.","Catch OperationCanceledException around the foreach that consumes the enumerable.","Materialize with ToList()/ToArray() while the token is still valid if you need the data afterwards.","Use the default token (CancellationToken.None) when no cancellation semantics are required."],"exampleFix":"// before\nvar xs = iterable.AsEnumerable(requestAbortedToken);\nSaveToDb(xs.ToList()); // enumerated later, token already cancelled\n// after\nvar xs = iterable.AsEnumerable(requestAbortedToken).ToList(); // enumerate now\nSaveToDb(xs);","handlingStrategy":"try-catch","validationCode":"if (token.IsCancellationRequested) throw new OperationCanceledException(token); // check before enumerating","typeGuard":null,"tryCatchPattern":"try\n{\n    foreach (var x in iterable.AsEnumerable(token))\n        Process(x);\n}\ncatch (OperationCanceledException) when (token.IsCancellationRequested)\n{\n    // enumeration cancelled\n}","preventionTips":["Remember iterator exceptions fire during MoveNext, not at AsEnumerable","Enumerate promptly or materialize with ToList while the token is valid","Don't cache enumerables tied to request-scoped tokens","Catch OperationCanceledException around any foreach consuming AsEnumerable(token)"],"tags":["cancellation","ienumerable","deferred-execution","languageext"],"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"}