{"record":{"id":"69dd7bcf90607d16","repo":"louthy/language-ext","slug":"operationcanceledexception-iteratorasync","errorCode":null,"errorMessage":"OperationCanceledException","messagePattern":"OperationCanceledException","errorType":"exception","errorClass":"OperationCanceledException","httpStatus":null,"severity":"warning","filePath":"LanguageExt.Core/Immutable Collections/IteratorAsync/IteratorAsync.cs","lineNumber":100,"sourceCode":"    /// this are to break the linked-list chain so that there isn't a big linked-list of objects in memory that\n    /// can't be garbage collected. \n    /// </summary>\n    /// <remarks>\n    /// Any other iterator references that came before this one will terminate at this point.  Splitting the\n    /// previous and subsequent iterators here. \n    /// </remarks>\n    /// <returns>New iterator that starts from the current iterator position.</returns>\n    public abstract IteratorAsync<A> Split();\n\n    /// <summary>\n    /// Create an `IEnumerable` from an `Iterator`\n    /// </summary>\n    [Pure]\n    public async IAsyncEnumerable<A> AsEnumerable([EnumeratorCancellation] CancellationToken token)\n    {\n        for (var ma = Clone(); !await ma.IsEmpty; ma = await ma.Tail)\n        {\n            if (token.IsCancellationRequested) throw new OperationCanceledException();\n            yield return await ma.Head;\n        }\n    }\n\n    /// <summary>\n    /// Functor map\n    /// </summary>\n    [Pure]\n    public IteratorAsync<B> Select<B>(Func<A, B> f) =>\n        Map(f);\n\n    /// <summary>\n    /// Functor map\n    /// </summary>\n    [Pure]\n    public IteratorAsync<B> Map<B>(Func<A, B> f)\n    {\n        return Go(this, f).GetIteratorAsync();","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/louthy/language-ext/blob/2f0e3628242889774d4141960a35671a0280051f/LanguageExt.Core/Immutable Collections/IteratorAsync/IteratorAsync.cs#L82-L118","documentation":"AsEnumerable on IteratorAsync checks the caller-supplied CancellationToken at each step and throws OperationCanceledException when cancellation was requested. Cooperatively cancels the enumeration loop.","triggerScenarios":"Enumerating an IteratorAsync via AsEnumerable(token) (directly or through GetAsyncEnumerator) while/after the CancellationToken is cancelled — e.g. request abort in ASP.NET Core or a timeout token firing mid-iteration.","commonSituations":"HTTP request cancelled by the client while streaming results; Task timeout token expiring during a long async enumeration; sharing a linked CTS and one branch cancelling.","solutions":["Pass a non-cancelled token when you need full enumeration; only pass request/timeout tokens when aborting early is intended.","Catch OperationCanceledException around the enumeration and treat it as normal cancellation (return 499/Cancelled, not 500).","Use `token.ThrowIfCancellationRequested()`-aware patterns and cts.Token registration for cleanup before rethrowing."],"exampleFix":"// before\nawait foreach (var x in it.AsEnumerable(ct)) Process(x);\n// after\ntry { await foreach (var x in it.AsEnumerable(ct)) Process(x); }\ncatch (OperationCanceledException) when (ct.IsCancellationRequested) { /* expected cancel */ }","handlingStrategy":"try-catch","validationCode":"if (ct.IsCancellationRequested) return; // check before starting enumeration\nawait foreach (var x in it.AsEnumerable(ct)) { ... }","typeGuard":"static bool IsUsableToken(CancellationToken ct) => ct.CanBeCanceled && !ct.IsCancellationRequested;","tryCatchPattern":"try { await foreach (var x in it.AsEnumerable(ct)) Process(x); }\ncatch (OperationCanceledException) when (ct.IsCancellationRequested) { /* cancellation is expected; clean up */ }","preventionTips":["Catch OperationCanceledException at the request boundary and map it to a cancelled/499 response, not an error.","Use `catch ... when (token.IsCancellationRequested)` so genuine bugs aren't swallowed.","Only pass request-scoped or timeout tokens when early abort is acceptable.","Register cleanup with token.Register before long enumerations."],"tags":["csharp","languageext","cancellation","async"],"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"}